From 43a8a0eca6e59aa42ea7a26c6acfbbfe67db41cd Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:44:03 +1100 Subject: [PATCH 001/100] [ES|QL] Update grammars (#199585) This PR updates the ES|QL grammars (lexer and parser) to match the latest version in Elasticsearch. --------- Co-authored-by: Stratoula Kalafateli --- packages/kbn-esql-ast/src/antlr/esql_lexer.g4 | 6 +- .../kbn-esql-ast/src/antlr/esql_lexer.interp | 13 +- .../kbn-esql-ast/src/antlr/esql_lexer.tokens | 188 ++- packages/kbn-esql-ast/src/antlr/esql_lexer.ts | 1168 +++++++++-------- .../kbn-esql-ast/src/antlr/esql_parser.g4 | 6 +- .../kbn-esql-ast/src/antlr/esql_parser.interp | 8 +- .../kbn-esql-ast/src/antlr/esql_parser.tokens | 188 ++- .../kbn-esql-ast/src/antlr/esql_parser.ts | 1117 ++++++++-------- .../validation.command.inlinestats.ts | 6 +- .../test_suites/validation.command.metrics.ts | 4 +- .../test_suites/validation.command.stats.ts | 6 +- .../esql_validation_meta_tests.json | 34 +- .../src/validation/validation.test.ts | 26 +- .../kbn-monaco/src/esql/lib/esql_theme.ts | 1 - 14 files changed, 1376 insertions(+), 1395 deletions(-) diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 index 0ffee7c0b0d4..ad17de2984ad 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 @@ -107,6 +107,8 @@ WS : [ \r\n\t]+ -> channel(HIDDEN) ; +COLON : ':'; + // // Expression - used by most command // @@ -209,8 +211,8 @@ MINUS : '-'; ASTERISK : '*'; SLASH : '/'; PERCENT : '%'; +EXPRESSION_COLON : {this.isDevVersion()}? COLON -> type(COLON); -MATCH : 'match'; NESTED_WHERE : WHERE -> type(WHERE); NAMED_OR_POSITIONAL_PARAM @@ -479,7 +481,7 @@ SHOW_WS mode SETTING_MODE; SETTING_CLOSING_BRACKET : CLOSING_BRACKET -> type(CLOSING_BRACKET), popMode; -COLON : ':'; +SETTING_COLON : COLON -> type(COLON); SETTING : (ASPERAND | DIGIT| DOT | LETTER | UNDERSCORE)+ diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp index 2566da379af7..8f9c5956dddd 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp @@ -23,6 +23,7 @@ null null null null +':' '|' null null @@ -62,7 +63,6 @@ null '*' '/' '%' -'match' null null ']' @@ -103,7 +103,6 @@ null null null null -':' null null null @@ -146,6 +145,7 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS +COLON PIPE QUOTED_STRING INTEGER_LITERAL @@ -185,7 +185,6 @@ MINUS ASTERISK SLASH PERCENT -MATCH NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET CLOSING_BRACKET @@ -226,7 +225,6 @@ INFO SHOW_LINE_COMMENT SHOW_MULTILINE_COMMENT SHOW_WS -COLON SETTING SETTING_LINE_COMMENT SETTTING_MULTILINE_COMMENT @@ -268,6 +266,7 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS +COLON PIPE DIGIT LETTER @@ -317,7 +316,7 @@ MINUS ASTERISK SLASH PERCENT -MATCH +EXPRESSION_COLON NESTED_WHERE NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET @@ -406,7 +405,7 @@ SHOW_LINE_COMMENT SHOW_MULTILINE_COMMENT SHOW_WS SETTING_CLOSING_BRACKET -COLON +SETTING_COLON SETTING SETTING_LINE_COMMENT SETTTING_MULTILINE_COMMENT @@ -466,4 +465,4 @@ METRICS_MODE CLOSING_METRICS_MODE atn: -[4, 0, 120, 1479, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 4, 19, 578, 8, 19, 11, 19, 12, 19, 579, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 588, 8, 20, 10, 20, 12, 20, 591, 9, 20, 1, 20, 3, 20, 594, 8, 20, 1, 20, 3, 20, 597, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 606, 8, 21, 10, 21, 12, 21, 609, 9, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 4, 22, 617, 8, 22, 11, 22, 12, 22, 618, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 28, 1, 28, 3, 28, 638, 8, 28, 1, 28, 4, 28, 641, 8, 28, 11, 28, 12, 28, 642, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 3, 31, 652, 8, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 659, 8, 33, 1, 34, 1, 34, 1, 34, 5, 34, 664, 8, 34, 10, 34, 12, 34, 667, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 675, 8, 34, 10, 34, 12, 34, 678, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 3, 34, 685, 8, 34, 1, 34, 3, 34, 688, 8, 34, 3, 34, 690, 8, 34, 1, 35, 4, 35, 693, 8, 35, 11, 35, 12, 35, 694, 1, 36, 4, 36, 698, 8, 36, 11, 36, 12, 36, 699, 1, 36, 1, 36, 5, 36, 704, 8, 36, 10, 36, 12, 36, 707, 9, 36, 1, 36, 1, 36, 4, 36, 711, 8, 36, 11, 36, 12, 36, 712, 1, 36, 4, 36, 716, 8, 36, 11, 36, 12, 36, 717, 1, 36, 1, 36, 5, 36, 722, 8, 36, 10, 36, 12, 36, 725, 9, 36, 3, 36, 727, 8, 36, 1, 36, 1, 36, 1, 36, 1, 36, 4, 36, 733, 8, 36, 11, 36, 12, 36, 734, 1, 36, 1, 36, 3, 36, 739, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 3, 74, 871, 8, 74, 1, 74, 5, 74, 874, 8, 74, 10, 74, 12, 74, 877, 9, 74, 1, 74, 1, 74, 4, 74, 881, 8, 74, 11, 74, 12, 74, 882, 3, 74, 885, 8, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 5, 77, 899, 8, 77, 10, 77, 12, 77, 902, 9, 77, 1, 77, 1, 77, 3, 77, 906, 8, 77, 1, 77, 4, 77, 909, 8, 77, 11, 77, 12, 77, 910, 3, 77, 913, 8, 77, 1, 78, 1, 78, 4, 78, 917, 8, 78, 11, 78, 12, 78, 918, 1, 78, 1, 78, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 3, 95, 996, 8, 95, 1, 96, 4, 96, 999, 8, 96, 11, 96, 12, 96, 1000, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 3, 107, 1050, 8, 107, 1, 108, 1, 108, 3, 108, 1054, 8, 108, 1, 108, 5, 108, 1057, 8, 108, 10, 108, 12, 108, 1060, 9, 108, 1, 108, 1, 108, 3, 108, 1064, 8, 108, 1, 108, 4, 108, 1067, 8, 108, 11, 108, 12, 108, 1068, 3, 108, 1071, 8, 108, 1, 109, 1, 109, 4, 109, 1075, 8, 109, 11, 109, 12, 109, 1076, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 129, 4, 129, 1162, 8, 129, 11, 129, 12, 129, 1163, 1, 129, 1, 129, 3, 129, 1168, 8, 129, 1, 129, 4, 129, 1171, 8, 129, 11, 129, 12, 129, 1172, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 4, 162, 1316, 8, 162, 11, 162, 12, 162, 1317, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 197, 2, 607, 676, 0, 198, 15, 1, 17, 2, 19, 3, 21, 4, 23, 5, 25, 6, 27, 7, 29, 8, 31, 9, 33, 10, 35, 11, 37, 12, 39, 13, 41, 14, 43, 15, 45, 16, 47, 17, 49, 18, 51, 19, 53, 20, 55, 21, 57, 22, 59, 23, 61, 24, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 0, 81, 0, 83, 25, 85, 26, 87, 27, 89, 28, 91, 29, 93, 30, 95, 31, 97, 32, 99, 33, 101, 34, 103, 35, 105, 36, 107, 37, 109, 38, 111, 39, 113, 40, 115, 41, 117, 42, 119, 43, 121, 44, 123, 45, 125, 46, 127, 47, 129, 48, 131, 49, 133, 50, 135, 51, 137, 52, 139, 53, 141, 54, 143, 55, 145, 56, 147, 57, 149, 58, 151, 59, 153, 60, 155, 61, 157, 62, 159, 63, 161, 0, 163, 64, 165, 65, 167, 66, 169, 67, 171, 0, 173, 68, 175, 69, 177, 70, 179, 71, 181, 0, 183, 0, 185, 72, 187, 73, 189, 74, 191, 0, 193, 0, 195, 0, 197, 0, 199, 0, 201, 0, 203, 75, 205, 0, 207, 76, 209, 0, 211, 0, 213, 77, 215, 78, 217, 79, 219, 0, 221, 0, 223, 0, 225, 0, 227, 0, 229, 0, 231, 0, 233, 80, 235, 81, 237, 82, 239, 83, 241, 0, 243, 0, 245, 0, 247, 0, 249, 0, 251, 0, 253, 84, 255, 0, 257, 85, 259, 86, 261, 87, 263, 0, 265, 0, 267, 88, 269, 89, 271, 0, 273, 90, 275, 0, 277, 91, 279, 92, 281, 93, 283, 0, 285, 0, 287, 0, 289, 0, 291, 0, 293, 0, 295, 0, 297, 0, 299, 0, 301, 94, 303, 95, 305, 96, 307, 0, 309, 0, 311, 0, 313, 0, 315, 0, 317, 0, 319, 97, 321, 98, 323, 99, 325, 0, 327, 100, 329, 101, 331, 102, 333, 103, 335, 0, 337, 104, 339, 105, 341, 106, 343, 107, 345, 108, 347, 0, 349, 0, 351, 0, 353, 0, 355, 0, 357, 0, 359, 0, 361, 109, 363, 110, 365, 111, 367, 0, 369, 0, 371, 0, 373, 0, 375, 112, 377, 113, 379, 114, 381, 0, 383, 0, 385, 0, 387, 115, 389, 116, 391, 117, 393, 0, 395, 0, 397, 118, 399, 119, 401, 120, 403, 0, 405, 0, 407, 0, 409, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1507, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 1, 61, 1, 0, 0, 0, 1, 83, 1, 0, 0, 0, 1, 85, 1, 0, 0, 0, 1, 87, 1, 0, 0, 0, 1, 89, 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 1, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 1, 97, 1, 0, 0, 0, 1, 99, 1, 0, 0, 0, 1, 101, 1, 0, 0, 0, 1, 103, 1, 0, 0, 0, 1, 105, 1, 0, 0, 0, 1, 107, 1, 0, 0, 0, 1, 109, 1, 0, 0, 0, 1, 111, 1, 0, 0, 0, 1, 113, 1, 0, 0, 0, 1, 115, 1, 0, 0, 0, 1, 117, 1, 0, 0, 0, 1, 119, 1, 0, 0, 0, 1, 121, 1, 0, 0, 0, 1, 123, 1, 0, 0, 0, 1, 125, 1, 0, 0, 0, 1, 127, 1, 0, 0, 0, 1, 129, 1, 0, 0, 0, 1, 131, 1, 0, 0, 0, 1, 133, 1, 0, 0, 0, 1, 135, 1, 0, 0, 0, 1, 137, 1, 0, 0, 0, 1, 139, 1, 0, 0, 0, 1, 141, 1, 0, 0, 0, 1, 143, 1, 0, 0, 0, 1, 145, 1, 0, 0, 0, 1, 147, 1, 0, 0, 0, 1, 149, 1, 0, 0, 0, 1, 151, 1, 0, 0, 0, 1, 153, 1, 0, 0, 0, 1, 155, 1, 0, 0, 0, 1, 157, 1, 0, 0, 0, 1, 159, 1, 0, 0, 0, 1, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 1, 165, 1, 0, 0, 0, 1, 167, 1, 0, 0, 0, 1, 169, 1, 0, 0, 0, 1, 173, 1, 0, 0, 0, 1, 175, 1, 0, 0, 0, 1, 177, 1, 0, 0, 0, 1, 179, 1, 0, 0, 0, 2, 181, 1, 0, 0, 0, 2, 183, 1, 0, 0, 0, 2, 185, 1, 0, 0, 0, 2, 187, 1, 0, 0, 0, 2, 189, 1, 0, 0, 0, 3, 191, 1, 0, 0, 0, 3, 193, 1, 0, 0, 0, 3, 195, 1, 0, 0, 0, 3, 197, 1, 0, 0, 0, 3, 199, 1, 0, 0, 0, 3, 201, 1, 0, 0, 0, 3, 203, 1, 0, 0, 0, 3, 207, 1, 0, 0, 0, 3, 209, 1, 0, 0, 0, 3, 211, 1, 0, 0, 0, 3, 213, 1, 0, 0, 0, 3, 215, 1, 0, 0, 0, 3, 217, 1, 0, 0, 0, 4, 219, 1, 0, 0, 0, 4, 221, 1, 0, 0, 0, 4, 223, 1, 0, 0, 0, 4, 225, 1, 0, 0, 0, 4, 227, 1, 0, 0, 0, 4, 233, 1, 0, 0, 0, 4, 235, 1, 0, 0, 0, 4, 237, 1, 0, 0, 0, 4, 239, 1, 0, 0, 0, 5, 241, 1, 0, 0, 0, 5, 243, 1, 0, 0, 0, 5, 245, 1, 0, 0, 0, 5, 247, 1, 0, 0, 0, 5, 249, 1, 0, 0, 0, 5, 251, 1, 0, 0, 0, 5, 253, 1, 0, 0, 0, 5, 255, 1, 0, 0, 0, 5, 257, 1, 0, 0, 0, 5, 259, 1, 0, 0, 0, 5, 261, 1, 0, 0, 0, 6, 263, 1, 0, 0, 0, 6, 265, 1, 0, 0, 0, 6, 267, 1, 0, 0, 0, 6, 269, 1, 0, 0, 0, 6, 273, 1, 0, 0, 0, 6, 275, 1, 0, 0, 0, 6, 277, 1, 0, 0, 0, 6, 279, 1, 0, 0, 0, 6, 281, 1, 0, 0, 0, 7, 283, 1, 0, 0, 0, 7, 285, 1, 0, 0, 0, 7, 287, 1, 0, 0, 0, 7, 289, 1, 0, 0, 0, 7, 291, 1, 0, 0, 0, 7, 293, 1, 0, 0, 0, 7, 295, 1, 0, 0, 0, 7, 297, 1, 0, 0, 0, 7, 299, 1, 0, 0, 0, 7, 301, 1, 0, 0, 0, 7, 303, 1, 0, 0, 0, 7, 305, 1, 0, 0, 0, 8, 307, 1, 0, 0, 0, 8, 309, 1, 0, 0, 0, 8, 311, 1, 0, 0, 0, 8, 313, 1, 0, 0, 0, 8, 315, 1, 0, 0, 0, 8, 317, 1, 0, 0, 0, 8, 319, 1, 0, 0, 0, 8, 321, 1, 0, 0, 0, 8, 323, 1, 0, 0, 0, 9, 325, 1, 0, 0, 0, 9, 327, 1, 0, 0, 0, 9, 329, 1, 0, 0, 0, 9, 331, 1, 0, 0, 0, 9, 333, 1, 0, 0, 0, 10, 335, 1, 0, 0, 0, 10, 337, 1, 0, 0, 0, 10, 339, 1, 0, 0, 0, 10, 341, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 10, 345, 1, 0, 0, 0, 11, 347, 1, 0, 0, 0, 11, 349, 1, 0, 0, 0, 11, 351, 1, 0, 0, 0, 11, 353, 1, 0, 0, 0, 11, 355, 1, 0, 0, 0, 11, 357, 1, 0, 0, 0, 11, 359, 1, 0, 0, 0, 11, 361, 1, 0, 0, 0, 11, 363, 1, 0, 0, 0, 11, 365, 1, 0, 0, 0, 12, 367, 1, 0, 0, 0, 12, 369, 1, 0, 0, 0, 12, 371, 1, 0, 0, 0, 12, 373, 1, 0, 0, 0, 12, 375, 1, 0, 0, 0, 12, 377, 1, 0, 0, 0, 12, 379, 1, 0, 0, 0, 13, 381, 1, 0, 0, 0, 13, 383, 1, 0, 0, 0, 13, 385, 1, 0, 0, 0, 13, 387, 1, 0, 0, 0, 13, 389, 1, 0, 0, 0, 13, 391, 1, 0, 0, 0, 14, 393, 1, 0, 0, 0, 14, 395, 1, 0, 0, 0, 14, 397, 1, 0, 0, 0, 14, 399, 1, 0, 0, 0, 14, 401, 1, 0, 0, 0, 14, 403, 1, 0, 0, 0, 14, 405, 1, 0, 0, 0, 14, 407, 1, 0, 0, 0, 14, 409, 1, 0, 0, 0, 15, 411, 1, 0, 0, 0, 17, 421, 1, 0, 0, 0, 19, 428, 1, 0, 0, 0, 21, 437, 1, 0, 0, 0, 23, 444, 1, 0, 0, 0, 25, 454, 1, 0, 0, 0, 27, 461, 1, 0, 0, 0, 29, 468, 1, 0, 0, 0, 31, 475, 1, 0, 0, 0, 33, 483, 1, 0, 0, 0, 35, 495, 1, 0, 0, 0, 37, 504, 1, 0, 0, 0, 39, 510, 1, 0, 0, 0, 41, 517, 1, 0, 0, 0, 43, 524, 1, 0, 0, 0, 45, 532, 1, 0, 0, 0, 47, 540, 1, 0, 0, 0, 49, 555, 1, 0, 0, 0, 51, 565, 1, 0, 0, 0, 53, 577, 1, 0, 0, 0, 55, 583, 1, 0, 0, 0, 57, 600, 1, 0, 0, 0, 59, 616, 1, 0, 0, 0, 61, 622, 1, 0, 0, 0, 63, 626, 1, 0, 0, 0, 65, 628, 1, 0, 0, 0, 67, 630, 1, 0, 0, 0, 69, 633, 1, 0, 0, 0, 71, 635, 1, 0, 0, 0, 73, 644, 1, 0, 0, 0, 75, 646, 1, 0, 0, 0, 77, 651, 1, 0, 0, 0, 79, 653, 1, 0, 0, 0, 81, 658, 1, 0, 0, 0, 83, 689, 1, 0, 0, 0, 85, 692, 1, 0, 0, 0, 87, 738, 1, 0, 0, 0, 89, 740, 1, 0, 0, 0, 91, 743, 1, 0, 0, 0, 93, 747, 1, 0, 0, 0, 95, 751, 1, 0, 0, 0, 97, 753, 1, 0, 0, 0, 99, 756, 1, 0, 0, 0, 101, 758, 1, 0, 0, 0, 103, 763, 1, 0, 0, 0, 105, 765, 1, 0, 0, 0, 107, 771, 1, 0, 0, 0, 109, 777, 1, 0, 0, 0, 111, 780, 1, 0, 0, 0, 113, 783, 1, 0, 0, 0, 115, 788, 1, 0, 0, 0, 117, 793, 1, 0, 0, 0, 119, 795, 1, 0, 0, 0, 121, 799, 1, 0, 0, 0, 123, 804, 1, 0, 0, 0, 125, 810, 1, 0, 0, 0, 127, 813, 1, 0, 0, 0, 129, 815, 1, 0, 0, 0, 131, 821, 1, 0, 0, 0, 133, 823, 1, 0, 0, 0, 135, 828, 1, 0, 0, 0, 137, 831, 1, 0, 0, 0, 139, 834, 1, 0, 0, 0, 141, 837, 1, 0, 0, 0, 143, 839, 1, 0, 0, 0, 145, 842, 1, 0, 0, 0, 147, 844, 1, 0, 0, 0, 149, 847, 1, 0, 0, 0, 151, 849, 1, 0, 0, 0, 153, 851, 1, 0, 0, 0, 155, 853, 1, 0, 0, 0, 157, 855, 1, 0, 0, 0, 159, 857, 1, 0, 0, 0, 161, 863, 1, 0, 0, 0, 163, 884, 1, 0, 0, 0, 165, 886, 1, 0, 0, 0, 167, 891, 1, 0, 0, 0, 169, 912, 1, 0, 0, 0, 171, 914, 1, 0, 0, 0, 173, 922, 1, 0, 0, 0, 175, 924, 1, 0, 0, 0, 177, 928, 1, 0, 0, 0, 179, 932, 1, 0, 0, 0, 181, 936, 1, 0, 0, 0, 183, 941, 1, 0, 0, 0, 185, 946, 1, 0, 0, 0, 187, 950, 1, 0, 0, 0, 189, 954, 1, 0, 0, 0, 191, 958, 1, 0, 0, 0, 193, 963, 1, 0, 0, 0, 195, 967, 1, 0, 0, 0, 197, 971, 1, 0, 0, 0, 199, 975, 1, 0, 0, 0, 201, 979, 1, 0, 0, 0, 203, 983, 1, 0, 0, 0, 205, 995, 1, 0, 0, 0, 207, 998, 1, 0, 0, 0, 209, 1002, 1, 0, 0, 0, 211, 1006, 1, 0, 0, 0, 213, 1010, 1, 0, 0, 0, 215, 1014, 1, 0, 0, 0, 217, 1018, 1, 0, 0, 0, 219, 1022, 1, 0, 0, 0, 221, 1027, 1, 0, 0, 0, 223, 1031, 1, 0, 0, 0, 225, 1035, 1, 0, 0, 0, 227, 1040, 1, 0, 0, 0, 229, 1049, 1, 0, 0, 0, 231, 1070, 1, 0, 0, 0, 233, 1074, 1, 0, 0, 0, 235, 1078, 1, 0, 0, 0, 237, 1082, 1, 0, 0, 0, 239, 1086, 1, 0, 0, 0, 241, 1090, 1, 0, 0, 0, 243, 1095, 1, 0, 0, 0, 245, 1099, 1, 0, 0, 0, 247, 1103, 1, 0, 0, 0, 249, 1107, 1, 0, 0, 0, 251, 1112, 1, 0, 0, 0, 253, 1117, 1, 0, 0, 0, 255, 1120, 1, 0, 0, 0, 257, 1124, 1, 0, 0, 0, 259, 1128, 1, 0, 0, 0, 261, 1132, 1, 0, 0, 0, 263, 1136, 1, 0, 0, 0, 265, 1141, 1, 0, 0, 0, 267, 1146, 1, 0, 0, 0, 269, 1151, 1, 0, 0, 0, 271, 1158, 1, 0, 0, 0, 273, 1167, 1, 0, 0, 0, 275, 1174, 1, 0, 0, 0, 277, 1178, 1, 0, 0, 0, 279, 1182, 1, 0, 0, 0, 281, 1186, 1, 0, 0, 0, 283, 1190, 1, 0, 0, 0, 285, 1196, 1, 0, 0, 0, 287, 1200, 1, 0, 0, 0, 289, 1204, 1, 0, 0, 0, 291, 1208, 1, 0, 0, 0, 293, 1212, 1, 0, 0, 0, 295, 1216, 1, 0, 0, 0, 297, 1220, 1, 0, 0, 0, 299, 1225, 1, 0, 0, 0, 301, 1230, 1, 0, 0, 0, 303, 1234, 1, 0, 0, 0, 305, 1238, 1, 0, 0, 0, 307, 1242, 1, 0, 0, 0, 309, 1247, 1, 0, 0, 0, 311, 1251, 1, 0, 0, 0, 313, 1256, 1, 0, 0, 0, 315, 1261, 1, 0, 0, 0, 317, 1265, 1, 0, 0, 0, 319, 1269, 1, 0, 0, 0, 321, 1273, 1, 0, 0, 0, 323, 1277, 1, 0, 0, 0, 325, 1281, 1, 0, 0, 0, 327, 1286, 1, 0, 0, 0, 329, 1291, 1, 0, 0, 0, 331, 1295, 1, 0, 0, 0, 333, 1299, 1, 0, 0, 0, 335, 1303, 1, 0, 0, 0, 337, 1308, 1, 0, 0, 0, 339, 1315, 1, 0, 0, 0, 341, 1319, 1, 0, 0, 0, 343, 1323, 1, 0, 0, 0, 345, 1327, 1, 0, 0, 0, 347, 1331, 1, 0, 0, 0, 349, 1336, 1, 0, 0, 0, 351, 1340, 1, 0, 0, 0, 353, 1344, 1, 0, 0, 0, 355, 1348, 1, 0, 0, 0, 357, 1353, 1, 0, 0, 0, 359, 1357, 1, 0, 0, 0, 361, 1361, 1, 0, 0, 0, 363, 1365, 1, 0, 0, 0, 365, 1369, 1, 0, 0, 0, 367, 1373, 1, 0, 0, 0, 369, 1379, 1, 0, 0, 0, 371, 1383, 1, 0, 0, 0, 373, 1387, 1, 0, 0, 0, 375, 1391, 1, 0, 0, 0, 377, 1395, 1, 0, 0, 0, 379, 1399, 1, 0, 0, 0, 381, 1403, 1, 0, 0, 0, 383, 1408, 1, 0, 0, 0, 385, 1414, 1, 0, 0, 0, 387, 1420, 1, 0, 0, 0, 389, 1424, 1, 0, 0, 0, 391, 1428, 1, 0, 0, 0, 393, 1432, 1, 0, 0, 0, 395, 1438, 1, 0, 0, 0, 397, 1444, 1, 0, 0, 0, 399, 1448, 1, 0, 0, 0, 401, 1452, 1, 0, 0, 0, 403, 1456, 1, 0, 0, 0, 405, 1462, 1, 0, 0, 0, 407, 1468, 1, 0, 0, 0, 409, 1474, 1, 0, 0, 0, 411, 412, 7, 0, 0, 0, 412, 413, 7, 1, 0, 0, 413, 414, 7, 2, 0, 0, 414, 415, 7, 2, 0, 0, 415, 416, 7, 3, 0, 0, 416, 417, 7, 4, 0, 0, 417, 418, 7, 5, 0, 0, 418, 419, 1, 0, 0, 0, 419, 420, 6, 0, 0, 0, 420, 16, 1, 0, 0, 0, 421, 422, 7, 0, 0, 0, 422, 423, 7, 6, 0, 0, 423, 424, 7, 7, 0, 0, 424, 425, 7, 8, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 6, 1, 1, 0, 427, 18, 1, 0, 0, 0, 428, 429, 7, 3, 0, 0, 429, 430, 7, 9, 0, 0, 430, 431, 7, 6, 0, 0, 431, 432, 7, 1, 0, 0, 432, 433, 7, 4, 0, 0, 433, 434, 7, 10, 0, 0, 434, 435, 1, 0, 0, 0, 435, 436, 6, 2, 2, 0, 436, 20, 1, 0, 0, 0, 437, 438, 7, 3, 0, 0, 438, 439, 7, 11, 0, 0, 439, 440, 7, 12, 0, 0, 440, 441, 7, 13, 0, 0, 441, 442, 1, 0, 0, 0, 442, 443, 6, 3, 0, 0, 443, 22, 1, 0, 0, 0, 444, 445, 7, 3, 0, 0, 445, 446, 7, 14, 0, 0, 446, 447, 7, 8, 0, 0, 447, 448, 7, 13, 0, 0, 448, 449, 7, 12, 0, 0, 449, 450, 7, 1, 0, 0, 450, 451, 7, 9, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 6, 4, 3, 0, 453, 24, 1, 0, 0, 0, 454, 455, 7, 15, 0, 0, 455, 456, 7, 6, 0, 0, 456, 457, 7, 7, 0, 0, 457, 458, 7, 16, 0, 0, 458, 459, 1, 0, 0, 0, 459, 460, 6, 5, 4, 0, 460, 26, 1, 0, 0, 0, 461, 462, 7, 17, 0, 0, 462, 463, 7, 6, 0, 0, 463, 464, 7, 7, 0, 0, 464, 465, 7, 18, 0, 0, 465, 466, 1, 0, 0, 0, 466, 467, 6, 6, 0, 0, 467, 28, 1, 0, 0, 0, 468, 469, 7, 18, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 3, 0, 0, 471, 472, 7, 8, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 6, 7, 1, 0, 474, 30, 1, 0, 0, 0, 475, 476, 7, 13, 0, 0, 476, 477, 7, 1, 0, 0, 477, 478, 7, 16, 0, 0, 478, 479, 7, 1, 0, 0, 479, 480, 7, 5, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 6, 8, 0, 0, 482, 32, 1, 0, 0, 0, 483, 484, 7, 16, 0, 0, 484, 485, 7, 11, 0, 0, 485, 486, 5, 95, 0, 0, 486, 487, 7, 3, 0, 0, 487, 488, 7, 14, 0, 0, 488, 489, 7, 8, 0, 0, 489, 490, 7, 12, 0, 0, 490, 491, 7, 9, 0, 0, 491, 492, 7, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 6, 9, 5, 0, 494, 34, 1, 0, 0, 0, 495, 496, 7, 6, 0, 0, 496, 497, 7, 3, 0, 0, 497, 498, 7, 9, 0, 0, 498, 499, 7, 12, 0, 0, 499, 500, 7, 16, 0, 0, 500, 501, 7, 3, 0, 0, 501, 502, 1, 0, 0, 0, 502, 503, 6, 10, 6, 0, 503, 36, 1, 0, 0, 0, 504, 505, 7, 6, 0, 0, 505, 506, 7, 7, 0, 0, 506, 507, 7, 19, 0, 0, 507, 508, 1, 0, 0, 0, 508, 509, 6, 11, 0, 0, 509, 38, 1, 0, 0, 0, 510, 511, 7, 2, 0, 0, 511, 512, 7, 10, 0, 0, 512, 513, 7, 7, 0, 0, 513, 514, 7, 19, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 6, 12, 7, 0, 516, 40, 1, 0, 0, 0, 517, 518, 7, 2, 0, 0, 518, 519, 7, 7, 0, 0, 519, 520, 7, 6, 0, 0, 520, 521, 7, 5, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 13, 0, 0, 523, 42, 1, 0, 0, 0, 524, 525, 7, 2, 0, 0, 525, 526, 7, 5, 0, 0, 526, 527, 7, 12, 0, 0, 527, 528, 7, 5, 0, 0, 528, 529, 7, 2, 0, 0, 529, 530, 1, 0, 0, 0, 530, 531, 6, 14, 0, 0, 531, 44, 1, 0, 0, 0, 532, 533, 7, 19, 0, 0, 533, 534, 7, 10, 0, 0, 534, 535, 7, 3, 0, 0, 535, 536, 7, 6, 0, 0, 536, 537, 7, 3, 0, 0, 537, 538, 1, 0, 0, 0, 538, 539, 6, 15, 0, 0, 539, 46, 1, 0, 0, 0, 540, 541, 4, 16, 0, 0, 541, 542, 7, 1, 0, 0, 542, 543, 7, 9, 0, 0, 543, 544, 7, 13, 0, 0, 544, 545, 7, 1, 0, 0, 545, 546, 7, 9, 0, 0, 546, 547, 7, 3, 0, 0, 547, 548, 7, 2, 0, 0, 548, 549, 7, 5, 0, 0, 549, 550, 7, 12, 0, 0, 550, 551, 7, 5, 0, 0, 551, 552, 7, 2, 0, 0, 552, 553, 1, 0, 0, 0, 553, 554, 6, 16, 0, 0, 554, 48, 1, 0, 0, 0, 555, 556, 4, 17, 1, 0, 556, 557, 7, 13, 0, 0, 557, 558, 7, 7, 0, 0, 558, 559, 7, 7, 0, 0, 559, 560, 7, 18, 0, 0, 560, 561, 7, 20, 0, 0, 561, 562, 7, 8, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 17, 8, 0, 564, 50, 1, 0, 0, 0, 565, 566, 4, 18, 2, 0, 566, 567, 7, 16, 0, 0, 567, 568, 7, 3, 0, 0, 568, 569, 7, 5, 0, 0, 569, 570, 7, 6, 0, 0, 570, 571, 7, 1, 0, 0, 571, 572, 7, 4, 0, 0, 572, 573, 7, 2, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 6, 18, 9, 0, 575, 52, 1, 0, 0, 0, 576, 578, 8, 21, 0, 0, 577, 576, 1, 0, 0, 0, 578, 579, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 581, 1, 0, 0, 0, 581, 582, 6, 19, 0, 0, 582, 54, 1, 0, 0, 0, 583, 584, 5, 47, 0, 0, 584, 585, 5, 47, 0, 0, 585, 589, 1, 0, 0, 0, 586, 588, 8, 22, 0, 0, 587, 586, 1, 0, 0, 0, 588, 591, 1, 0, 0, 0, 589, 587, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 593, 1, 0, 0, 0, 591, 589, 1, 0, 0, 0, 592, 594, 5, 13, 0, 0, 593, 592, 1, 0, 0, 0, 593, 594, 1, 0, 0, 0, 594, 596, 1, 0, 0, 0, 595, 597, 5, 10, 0, 0, 596, 595, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 598, 1, 0, 0, 0, 598, 599, 6, 20, 10, 0, 599, 56, 1, 0, 0, 0, 600, 601, 5, 47, 0, 0, 601, 602, 5, 42, 0, 0, 602, 607, 1, 0, 0, 0, 603, 606, 3, 57, 21, 0, 604, 606, 9, 0, 0, 0, 605, 603, 1, 0, 0, 0, 605, 604, 1, 0, 0, 0, 606, 609, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 608, 610, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 611, 5, 42, 0, 0, 611, 612, 5, 47, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 6, 21, 10, 0, 614, 58, 1, 0, 0, 0, 615, 617, 7, 23, 0, 0, 616, 615, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 6, 22, 10, 0, 621, 60, 1, 0, 0, 0, 622, 623, 5, 124, 0, 0, 623, 624, 1, 0, 0, 0, 624, 625, 6, 23, 11, 0, 625, 62, 1, 0, 0, 0, 626, 627, 7, 24, 0, 0, 627, 64, 1, 0, 0, 0, 628, 629, 7, 25, 0, 0, 629, 66, 1, 0, 0, 0, 630, 631, 5, 92, 0, 0, 631, 632, 7, 26, 0, 0, 632, 68, 1, 0, 0, 0, 633, 634, 8, 27, 0, 0, 634, 70, 1, 0, 0, 0, 635, 637, 7, 3, 0, 0, 636, 638, 7, 28, 0, 0, 637, 636, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 640, 1, 0, 0, 0, 639, 641, 3, 63, 24, 0, 640, 639, 1, 0, 0, 0, 641, 642, 1, 0, 0, 0, 642, 640, 1, 0, 0, 0, 642, 643, 1, 0, 0, 0, 643, 72, 1, 0, 0, 0, 644, 645, 5, 64, 0, 0, 645, 74, 1, 0, 0, 0, 646, 647, 5, 96, 0, 0, 647, 76, 1, 0, 0, 0, 648, 652, 8, 29, 0, 0, 649, 650, 5, 96, 0, 0, 650, 652, 5, 96, 0, 0, 651, 648, 1, 0, 0, 0, 651, 649, 1, 0, 0, 0, 652, 78, 1, 0, 0, 0, 653, 654, 5, 95, 0, 0, 654, 80, 1, 0, 0, 0, 655, 659, 3, 65, 25, 0, 656, 659, 3, 63, 24, 0, 657, 659, 3, 79, 32, 0, 658, 655, 1, 0, 0, 0, 658, 656, 1, 0, 0, 0, 658, 657, 1, 0, 0, 0, 659, 82, 1, 0, 0, 0, 660, 665, 5, 34, 0, 0, 661, 664, 3, 67, 26, 0, 662, 664, 3, 69, 27, 0, 663, 661, 1, 0, 0, 0, 663, 662, 1, 0, 0, 0, 664, 667, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 665, 666, 1, 0, 0, 0, 666, 668, 1, 0, 0, 0, 667, 665, 1, 0, 0, 0, 668, 690, 5, 34, 0, 0, 669, 670, 5, 34, 0, 0, 670, 671, 5, 34, 0, 0, 671, 672, 5, 34, 0, 0, 672, 676, 1, 0, 0, 0, 673, 675, 8, 22, 0, 0, 674, 673, 1, 0, 0, 0, 675, 678, 1, 0, 0, 0, 676, 677, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 677, 679, 1, 0, 0, 0, 678, 676, 1, 0, 0, 0, 679, 680, 5, 34, 0, 0, 680, 681, 5, 34, 0, 0, 681, 682, 5, 34, 0, 0, 682, 684, 1, 0, 0, 0, 683, 685, 5, 34, 0, 0, 684, 683, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 687, 1, 0, 0, 0, 686, 688, 5, 34, 0, 0, 687, 686, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 690, 1, 0, 0, 0, 689, 660, 1, 0, 0, 0, 689, 669, 1, 0, 0, 0, 690, 84, 1, 0, 0, 0, 691, 693, 3, 63, 24, 0, 692, 691, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 692, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 86, 1, 0, 0, 0, 696, 698, 3, 63, 24, 0, 697, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 697, 1, 0, 0, 0, 699, 700, 1, 0, 0, 0, 700, 701, 1, 0, 0, 0, 701, 705, 3, 103, 44, 0, 702, 704, 3, 63, 24, 0, 703, 702, 1, 0, 0, 0, 704, 707, 1, 0, 0, 0, 705, 703, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 739, 1, 0, 0, 0, 707, 705, 1, 0, 0, 0, 708, 710, 3, 103, 44, 0, 709, 711, 3, 63, 24, 0, 710, 709, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 710, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 739, 1, 0, 0, 0, 714, 716, 3, 63, 24, 0, 715, 714, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 715, 1, 0, 0, 0, 717, 718, 1, 0, 0, 0, 718, 726, 1, 0, 0, 0, 719, 723, 3, 103, 44, 0, 720, 722, 3, 63, 24, 0, 721, 720, 1, 0, 0, 0, 722, 725, 1, 0, 0, 0, 723, 721, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 727, 1, 0, 0, 0, 725, 723, 1, 0, 0, 0, 726, 719, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 729, 3, 71, 28, 0, 729, 739, 1, 0, 0, 0, 730, 732, 3, 103, 44, 0, 731, 733, 3, 63, 24, 0, 732, 731, 1, 0, 0, 0, 733, 734, 1, 0, 0, 0, 734, 732, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 736, 1, 0, 0, 0, 736, 737, 3, 71, 28, 0, 737, 739, 1, 0, 0, 0, 738, 697, 1, 0, 0, 0, 738, 708, 1, 0, 0, 0, 738, 715, 1, 0, 0, 0, 738, 730, 1, 0, 0, 0, 739, 88, 1, 0, 0, 0, 740, 741, 7, 30, 0, 0, 741, 742, 7, 31, 0, 0, 742, 90, 1, 0, 0, 0, 743, 744, 7, 12, 0, 0, 744, 745, 7, 9, 0, 0, 745, 746, 7, 0, 0, 0, 746, 92, 1, 0, 0, 0, 747, 748, 7, 12, 0, 0, 748, 749, 7, 2, 0, 0, 749, 750, 7, 4, 0, 0, 750, 94, 1, 0, 0, 0, 751, 752, 5, 61, 0, 0, 752, 96, 1, 0, 0, 0, 753, 754, 5, 58, 0, 0, 754, 755, 5, 58, 0, 0, 755, 98, 1, 0, 0, 0, 756, 757, 5, 44, 0, 0, 757, 100, 1, 0, 0, 0, 758, 759, 7, 0, 0, 0, 759, 760, 7, 3, 0, 0, 760, 761, 7, 2, 0, 0, 761, 762, 7, 4, 0, 0, 762, 102, 1, 0, 0, 0, 763, 764, 5, 46, 0, 0, 764, 104, 1, 0, 0, 0, 765, 766, 7, 15, 0, 0, 766, 767, 7, 12, 0, 0, 767, 768, 7, 13, 0, 0, 768, 769, 7, 2, 0, 0, 769, 770, 7, 3, 0, 0, 770, 106, 1, 0, 0, 0, 771, 772, 7, 15, 0, 0, 772, 773, 7, 1, 0, 0, 773, 774, 7, 6, 0, 0, 774, 775, 7, 2, 0, 0, 775, 776, 7, 5, 0, 0, 776, 108, 1, 0, 0, 0, 777, 778, 7, 1, 0, 0, 778, 779, 7, 9, 0, 0, 779, 110, 1, 0, 0, 0, 780, 781, 7, 1, 0, 0, 781, 782, 7, 2, 0, 0, 782, 112, 1, 0, 0, 0, 783, 784, 7, 13, 0, 0, 784, 785, 7, 12, 0, 0, 785, 786, 7, 2, 0, 0, 786, 787, 7, 5, 0, 0, 787, 114, 1, 0, 0, 0, 788, 789, 7, 13, 0, 0, 789, 790, 7, 1, 0, 0, 790, 791, 7, 18, 0, 0, 791, 792, 7, 3, 0, 0, 792, 116, 1, 0, 0, 0, 793, 794, 5, 40, 0, 0, 794, 118, 1, 0, 0, 0, 795, 796, 7, 9, 0, 0, 796, 797, 7, 7, 0, 0, 797, 798, 7, 5, 0, 0, 798, 120, 1, 0, 0, 0, 799, 800, 7, 9, 0, 0, 800, 801, 7, 20, 0, 0, 801, 802, 7, 13, 0, 0, 802, 803, 7, 13, 0, 0, 803, 122, 1, 0, 0, 0, 804, 805, 7, 9, 0, 0, 805, 806, 7, 20, 0, 0, 806, 807, 7, 13, 0, 0, 807, 808, 7, 13, 0, 0, 808, 809, 7, 2, 0, 0, 809, 124, 1, 0, 0, 0, 810, 811, 7, 7, 0, 0, 811, 812, 7, 6, 0, 0, 812, 126, 1, 0, 0, 0, 813, 814, 5, 63, 0, 0, 814, 128, 1, 0, 0, 0, 815, 816, 7, 6, 0, 0, 816, 817, 7, 13, 0, 0, 817, 818, 7, 1, 0, 0, 818, 819, 7, 18, 0, 0, 819, 820, 7, 3, 0, 0, 820, 130, 1, 0, 0, 0, 821, 822, 5, 41, 0, 0, 822, 132, 1, 0, 0, 0, 823, 824, 7, 5, 0, 0, 824, 825, 7, 6, 0, 0, 825, 826, 7, 20, 0, 0, 826, 827, 7, 3, 0, 0, 827, 134, 1, 0, 0, 0, 828, 829, 5, 61, 0, 0, 829, 830, 5, 61, 0, 0, 830, 136, 1, 0, 0, 0, 831, 832, 5, 61, 0, 0, 832, 833, 5, 126, 0, 0, 833, 138, 1, 0, 0, 0, 834, 835, 5, 33, 0, 0, 835, 836, 5, 61, 0, 0, 836, 140, 1, 0, 0, 0, 837, 838, 5, 60, 0, 0, 838, 142, 1, 0, 0, 0, 839, 840, 5, 60, 0, 0, 840, 841, 5, 61, 0, 0, 841, 144, 1, 0, 0, 0, 842, 843, 5, 62, 0, 0, 843, 146, 1, 0, 0, 0, 844, 845, 5, 62, 0, 0, 845, 846, 5, 61, 0, 0, 846, 148, 1, 0, 0, 0, 847, 848, 5, 43, 0, 0, 848, 150, 1, 0, 0, 0, 849, 850, 5, 45, 0, 0, 850, 152, 1, 0, 0, 0, 851, 852, 5, 42, 0, 0, 852, 154, 1, 0, 0, 0, 853, 854, 5, 47, 0, 0, 854, 156, 1, 0, 0, 0, 855, 856, 5, 37, 0, 0, 856, 158, 1, 0, 0, 0, 857, 858, 7, 16, 0, 0, 858, 859, 7, 12, 0, 0, 859, 860, 7, 5, 0, 0, 860, 861, 7, 4, 0, 0, 861, 862, 7, 10, 0, 0, 862, 160, 1, 0, 0, 0, 863, 864, 3, 45, 15, 0, 864, 865, 1, 0, 0, 0, 865, 866, 6, 73, 12, 0, 866, 162, 1, 0, 0, 0, 867, 870, 3, 127, 56, 0, 868, 871, 3, 65, 25, 0, 869, 871, 3, 79, 32, 0, 870, 868, 1, 0, 0, 0, 870, 869, 1, 0, 0, 0, 871, 875, 1, 0, 0, 0, 872, 874, 3, 81, 33, 0, 873, 872, 1, 0, 0, 0, 874, 877, 1, 0, 0, 0, 875, 873, 1, 0, 0, 0, 875, 876, 1, 0, 0, 0, 876, 885, 1, 0, 0, 0, 877, 875, 1, 0, 0, 0, 878, 880, 3, 127, 56, 0, 879, 881, 3, 63, 24, 0, 880, 879, 1, 0, 0, 0, 881, 882, 1, 0, 0, 0, 882, 880, 1, 0, 0, 0, 882, 883, 1, 0, 0, 0, 883, 885, 1, 0, 0, 0, 884, 867, 1, 0, 0, 0, 884, 878, 1, 0, 0, 0, 885, 164, 1, 0, 0, 0, 886, 887, 5, 91, 0, 0, 887, 888, 1, 0, 0, 0, 888, 889, 6, 75, 0, 0, 889, 890, 6, 75, 0, 0, 890, 166, 1, 0, 0, 0, 891, 892, 5, 93, 0, 0, 892, 893, 1, 0, 0, 0, 893, 894, 6, 76, 11, 0, 894, 895, 6, 76, 11, 0, 895, 168, 1, 0, 0, 0, 896, 900, 3, 65, 25, 0, 897, 899, 3, 81, 33, 0, 898, 897, 1, 0, 0, 0, 899, 902, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 913, 1, 0, 0, 0, 902, 900, 1, 0, 0, 0, 903, 906, 3, 79, 32, 0, 904, 906, 3, 73, 29, 0, 905, 903, 1, 0, 0, 0, 905, 904, 1, 0, 0, 0, 906, 908, 1, 0, 0, 0, 907, 909, 3, 81, 33, 0, 908, 907, 1, 0, 0, 0, 909, 910, 1, 0, 0, 0, 910, 908, 1, 0, 0, 0, 910, 911, 1, 0, 0, 0, 911, 913, 1, 0, 0, 0, 912, 896, 1, 0, 0, 0, 912, 905, 1, 0, 0, 0, 913, 170, 1, 0, 0, 0, 914, 916, 3, 75, 30, 0, 915, 917, 3, 77, 31, 0, 916, 915, 1, 0, 0, 0, 917, 918, 1, 0, 0, 0, 918, 916, 1, 0, 0, 0, 918, 919, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 921, 3, 75, 30, 0, 921, 172, 1, 0, 0, 0, 922, 923, 3, 171, 78, 0, 923, 174, 1, 0, 0, 0, 924, 925, 3, 55, 20, 0, 925, 926, 1, 0, 0, 0, 926, 927, 6, 80, 10, 0, 927, 176, 1, 0, 0, 0, 928, 929, 3, 57, 21, 0, 929, 930, 1, 0, 0, 0, 930, 931, 6, 81, 10, 0, 931, 178, 1, 0, 0, 0, 932, 933, 3, 59, 22, 0, 933, 934, 1, 0, 0, 0, 934, 935, 6, 82, 10, 0, 935, 180, 1, 0, 0, 0, 936, 937, 3, 165, 75, 0, 937, 938, 1, 0, 0, 0, 938, 939, 6, 83, 13, 0, 939, 940, 6, 83, 14, 0, 940, 182, 1, 0, 0, 0, 941, 942, 3, 61, 23, 0, 942, 943, 1, 0, 0, 0, 943, 944, 6, 84, 15, 0, 944, 945, 6, 84, 11, 0, 945, 184, 1, 0, 0, 0, 946, 947, 3, 59, 22, 0, 947, 948, 1, 0, 0, 0, 948, 949, 6, 85, 10, 0, 949, 186, 1, 0, 0, 0, 950, 951, 3, 55, 20, 0, 951, 952, 1, 0, 0, 0, 952, 953, 6, 86, 10, 0, 953, 188, 1, 0, 0, 0, 954, 955, 3, 57, 21, 0, 955, 956, 1, 0, 0, 0, 956, 957, 6, 87, 10, 0, 957, 190, 1, 0, 0, 0, 958, 959, 3, 61, 23, 0, 959, 960, 1, 0, 0, 0, 960, 961, 6, 88, 15, 0, 961, 962, 6, 88, 11, 0, 962, 192, 1, 0, 0, 0, 963, 964, 3, 165, 75, 0, 964, 965, 1, 0, 0, 0, 965, 966, 6, 89, 13, 0, 966, 194, 1, 0, 0, 0, 967, 968, 3, 167, 76, 0, 968, 969, 1, 0, 0, 0, 969, 970, 6, 90, 16, 0, 970, 196, 1, 0, 0, 0, 971, 972, 3, 337, 161, 0, 972, 973, 1, 0, 0, 0, 973, 974, 6, 91, 17, 0, 974, 198, 1, 0, 0, 0, 975, 976, 3, 99, 42, 0, 976, 977, 1, 0, 0, 0, 977, 978, 6, 92, 18, 0, 978, 200, 1, 0, 0, 0, 979, 980, 3, 95, 40, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 93, 19, 0, 982, 202, 1, 0, 0, 0, 983, 984, 7, 16, 0, 0, 984, 985, 7, 3, 0, 0, 985, 986, 7, 5, 0, 0, 986, 987, 7, 12, 0, 0, 987, 988, 7, 0, 0, 0, 988, 989, 7, 12, 0, 0, 989, 990, 7, 5, 0, 0, 990, 991, 7, 12, 0, 0, 991, 204, 1, 0, 0, 0, 992, 996, 8, 32, 0, 0, 993, 994, 5, 47, 0, 0, 994, 996, 8, 33, 0, 0, 995, 992, 1, 0, 0, 0, 995, 993, 1, 0, 0, 0, 996, 206, 1, 0, 0, 0, 997, 999, 3, 205, 95, 0, 998, 997, 1, 0, 0, 0, 999, 1000, 1, 0, 0, 0, 1000, 998, 1, 0, 0, 0, 1000, 1001, 1, 0, 0, 0, 1001, 208, 1, 0, 0, 0, 1002, 1003, 3, 207, 96, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 6, 97, 20, 0, 1005, 210, 1, 0, 0, 0, 1006, 1007, 3, 83, 34, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 6, 98, 21, 0, 1009, 212, 1, 0, 0, 0, 1010, 1011, 3, 55, 20, 0, 1011, 1012, 1, 0, 0, 0, 1012, 1013, 6, 99, 10, 0, 1013, 214, 1, 0, 0, 0, 1014, 1015, 3, 57, 21, 0, 1015, 1016, 1, 0, 0, 0, 1016, 1017, 6, 100, 10, 0, 1017, 216, 1, 0, 0, 0, 1018, 1019, 3, 59, 22, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 6, 101, 10, 0, 1021, 218, 1, 0, 0, 0, 1022, 1023, 3, 61, 23, 0, 1023, 1024, 1, 0, 0, 0, 1024, 1025, 6, 102, 15, 0, 1025, 1026, 6, 102, 11, 0, 1026, 220, 1, 0, 0, 0, 1027, 1028, 3, 103, 44, 0, 1028, 1029, 1, 0, 0, 0, 1029, 1030, 6, 103, 22, 0, 1030, 222, 1, 0, 0, 0, 1031, 1032, 3, 99, 42, 0, 1032, 1033, 1, 0, 0, 0, 1033, 1034, 6, 104, 18, 0, 1034, 224, 1, 0, 0, 0, 1035, 1036, 4, 105, 3, 0, 1036, 1037, 3, 127, 56, 0, 1037, 1038, 1, 0, 0, 0, 1038, 1039, 6, 105, 23, 0, 1039, 226, 1, 0, 0, 0, 1040, 1041, 4, 106, 4, 0, 1041, 1042, 3, 163, 74, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1044, 6, 106, 24, 0, 1044, 228, 1, 0, 0, 0, 1045, 1050, 3, 65, 25, 0, 1046, 1050, 3, 63, 24, 0, 1047, 1050, 3, 79, 32, 0, 1048, 1050, 3, 153, 69, 0, 1049, 1045, 1, 0, 0, 0, 1049, 1046, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1049, 1048, 1, 0, 0, 0, 1050, 230, 1, 0, 0, 0, 1051, 1054, 3, 65, 25, 0, 1052, 1054, 3, 153, 69, 0, 1053, 1051, 1, 0, 0, 0, 1053, 1052, 1, 0, 0, 0, 1054, 1058, 1, 0, 0, 0, 1055, 1057, 3, 229, 107, 0, 1056, 1055, 1, 0, 0, 0, 1057, 1060, 1, 0, 0, 0, 1058, 1056, 1, 0, 0, 0, 1058, 1059, 1, 0, 0, 0, 1059, 1071, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1061, 1064, 3, 79, 32, 0, 1062, 1064, 3, 73, 29, 0, 1063, 1061, 1, 0, 0, 0, 1063, 1062, 1, 0, 0, 0, 1064, 1066, 1, 0, 0, 0, 1065, 1067, 3, 229, 107, 0, 1066, 1065, 1, 0, 0, 0, 1067, 1068, 1, 0, 0, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1069, 1, 0, 0, 0, 1069, 1071, 1, 0, 0, 0, 1070, 1053, 1, 0, 0, 0, 1070, 1063, 1, 0, 0, 0, 1071, 232, 1, 0, 0, 0, 1072, 1075, 3, 231, 108, 0, 1073, 1075, 3, 171, 78, 0, 1074, 1072, 1, 0, 0, 0, 1074, 1073, 1, 0, 0, 0, 1075, 1076, 1, 0, 0, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1077, 1, 0, 0, 0, 1077, 234, 1, 0, 0, 0, 1078, 1079, 3, 55, 20, 0, 1079, 1080, 1, 0, 0, 0, 1080, 1081, 6, 110, 10, 0, 1081, 236, 1, 0, 0, 0, 1082, 1083, 3, 57, 21, 0, 1083, 1084, 1, 0, 0, 0, 1084, 1085, 6, 111, 10, 0, 1085, 238, 1, 0, 0, 0, 1086, 1087, 3, 59, 22, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 6, 112, 10, 0, 1089, 240, 1, 0, 0, 0, 1090, 1091, 3, 61, 23, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1093, 6, 113, 15, 0, 1093, 1094, 6, 113, 11, 0, 1094, 242, 1, 0, 0, 0, 1095, 1096, 3, 95, 40, 0, 1096, 1097, 1, 0, 0, 0, 1097, 1098, 6, 114, 19, 0, 1098, 244, 1, 0, 0, 0, 1099, 1100, 3, 99, 42, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1102, 6, 115, 18, 0, 1102, 246, 1, 0, 0, 0, 1103, 1104, 3, 103, 44, 0, 1104, 1105, 1, 0, 0, 0, 1105, 1106, 6, 116, 22, 0, 1106, 248, 1, 0, 0, 0, 1107, 1108, 4, 117, 5, 0, 1108, 1109, 3, 127, 56, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1111, 6, 117, 23, 0, 1111, 250, 1, 0, 0, 0, 1112, 1113, 4, 118, 6, 0, 1113, 1114, 3, 163, 74, 0, 1114, 1115, 1, 0, 0, 0, 1115, 1116, 6, 118, 24, 0, 1116, 252, 1, 0, 0, 0, 1117, 1118, 7, 12, 0, 0, 1118, 1119, 7, 2, 0, 0, 1119, 254, 1, 0, 0, 0, 1120, 1121, 3, 233, 109, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 6, 120, 25, 0, 1123, 256, 1, 0, 0, 0, 1124, 1125, 3, 55, 20, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 121, 10, 0, 1127, 258, 1, 0, 0, 0, 1128, 1129, 3, 57, 21, 0, 1129, 1130, 1, 0, 0, 0, 1130, 1131, 6, 122, 10, 0, 1131, 260, 1, 0, 0, 0, 1132, 1133, 3, 59, 22, 0, 1133, 1134, 1, 0, 0, 0, 1134, 1135, 6, 123, 10, 0, 1135, 262, 1, 0, 0, 0, 1136, 1137, 3, 61, 23, 0, 1137, 1138, 1, 0, 0, 0, 1138, 1139, 6, 124, 15, 0, 1139, 1140, 6, 124, 11, 0, 1140, 264, 1, 0, 0, 0, 1141, 1142, 3, 165, 75, 0, 1142, 1143, 1, 0, 0, 0, 1143, 1144, 6, 125, 13, 0, 1144, 1145, 6, 125, 26, 0, 1145, 266, 1, 0, 0, 0, 1146, 1147, 7, 7, 0, 0, 1147, 1148, 7, 9, 0, 0, 1148, 1149, 1, 0, 0, 0, 1149, 1150, 6, 126, 27, 0, 1150, 268, 1, 0, 0, 0, 1151, 1152, 7, 19, 0, 0, 1152, 1153, 7, 1, 0, 0, 1153, 1154, 7, 5, 0, 0, 1154, 1155, 7, 10, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1157, 6, 127, 27, 0, 1157, 270, 1, 0, 0, 0, 1158, 1159, 8, 34, 0, 0, 1159, 272, 1, 0, 0, 0, 1160, 1162, 3, 271, 128, 0, 1161, 1160, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 1161, 1, 0, 0, 0, 1163, 1164, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 1166, 3, 337, 161, 0, 1166, 1168, 1, 0, 0, 0, 1167, 1161, 1, 0, 0, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1170, 1, 0, 0, 0, 1169, 1171, 3, 271, 128, 0, 1170, 1169, 1, 0, 0, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 274, 1, 0, 0, 0, 1174, 1175, 3, 273, 129, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 130, 28, 0, 1177, 276, 1, 0, 0, 0, 1178, 1179, 3, 55, 20, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 131, 10, 0, 1181, 278, 1, 0, 0, 0, 1182, 1183, 3, 57, 21, 0, 1183, 1184, 1, 0, 0, 0, 1184, 1185, 6, 132, 10, 0, 1185, 280, 1, 0, 0, 0, 1186, 1187, 3, 59, 22, 0, 1187, 1188, 1, 0, 0, 0, 1188, 1189, 6, 133, 10, 0, 1189, 282, 1, 0, 0, 0, 1190, 1191, 3, 61, 23, 0, 1191, 1192, 1, 0, 0, 0, 1192, 1193, 6, 134, 15, 0, 1193, 1194, 6, 134, 11, 0, 1194, 1195, 6, 134, 11, 0, 1195, 284, 1, 0, 0, 0, 1196, 1197, 3, 95, 40, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 135, 19, 0, 1199, 286, 1, 0, 0, 0, 1200, 1201, 3, 99, 42, 0, 1201, 1202, 1, 0, 0, 0, 1202, 1203, 6, 136, 18, 0, 1203, 288, 1, 0, 0, 0, 1204, 1205, 3, 103, 44, 0, 1205, 1206, 1, 0, 0, 0, 1206, 1207, 6, 137, 22, 0, 1207, 290, 1, 0, 0, 0, 1208, 1209, 3, 269, 127, 0, 1209, 1210, 1, 0, 0, 0, 1210, 1211, 6, 138, 29, 0, 1211, 292, 1, 0, 0, 0, 1212, 1213, 3, 233, 109, 0, 1213, 1214, 1, 0, 0, 0, 1214, 1215, 6, 139, 25, 0, 1215, 294, 1, 0, 0, 0, 1216, 1217, 3, 173, 79, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 140, 30, 0, 1219, 296, 1, 0, 0, 0, 1220, 1221, 4, 141, 7, 0, 1221, 1222, 3, 127, 56, 0, 1222, 1223, 1, 0, 0, 0, 1223, 1224, 6, 141, 23, 0, 1224, 298, 1, 0, 0, 0, 1225, 1226, 4, 142, 8, 0, 1226, 1227, 3, 163, 74, 0, 1227, 1228, 1, 0, 0, 0, 1228, 1229, 6, 142, 24, 0, 1229, 300, 1, 0, 0, 0, 1230, 1231, 3, 55, 20, 0, 1231, 1232, 1, 0, 0, 0, 1232, 1233, 6, 143, 10, 0, 1233, 302, 1, 0, 0, 0, 1234, 1235, 3, 57, 21, 0, 1235, 1236, 1, 0, 0, 0, 1236, 1237, 6, 144, 10, 0, 1237, 304, 1, 0, 0, 0, 1238, 1239, 3, 59, 22, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 6, 145, 10, 0, 1241, 306, 1, 0, 0, 0, 1242, 1243, 3, 61, 23, 0, 1243, 1244, 1, 0, 0, 0, 1244, 1245, 6, 146, 15, 0, 1245, 1246, 6, 146, 11, 0, 1246, 308, 1, 0, 0, 0, 1247, 1248, 3, 103, 44, 0, 1248, 1249, 1, 0, 0, 0, 1249, 1250, 6, 147, 22, 0, 1250, 310, 1, 0, 0, 0, 1251, 1252, 4, 148, 9, 0, 1252, 1253, 3, 127, 56, 0, 1253, 1254, 1, 0, 0, 0, 1254, 1255, 6, 148, 23, 0, 1255, 312, 1, 0, 0, 0, 1256, 1257, 4, 149, 10, 0, 1257, 1258, 3, 163, 74, 0, 1258, 1259, 1, 0, 0, 0, 1259, 1260, 6, 149, 24, 0, 1260, 314, 1, 0, 0, 0, 1261, 1262, 3, 173, 79, 0, 1262, 1263, 1, 0, 0, 0, 1263, 1264, 6, 150, 30, 0, 1264, 316, 1, 0, 0, 0, 1265, 1266, 3, 169, 77, 0, 1266, 1267, 1, 0, 0, 0, 1267, 1268, 6, 151, 31, 0, 1268, 318, 1, 0, 0, 0, 1269, 1270, 3, 55, 20, 0, 1270, 1271, 1, 0, 0, 0, 1271, 1272, 6, 152, 10, 0, 1272, 320, 1, 0, 0, 0, 1273, 1274, 3, 57, 21, 0, 1274, 1275, 1, 0, 0, 0, 1275, 1276, 6, 153, 10, 0, 1276, 322, 1, 0, 0, 0, 1277, 1278, 3, 59, 22, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1280, 6, 154, 10, 0, 1280, 324, 1, 0, 0, 0, 1281, 1282, 3, 61, 23, 0, 1282, 1283, 1, 0, 0, 0, 1283, 1284, 6, 155, 15, 0, 1284, 1285, 6, 155, 11, 0, 1285, 326, 1, 0, 0, 0, 1286, 1287, 7, 1, 0, 0, 1287, 1288, 7, 9, 0, 0, 1288, 1289, 7, 15, 0, 0, 1289, 1290, 7, 7, 0, 0, 1290, 328, 1, 0, 0, 0, 1291, 1292, 3, 55, 20, 0, 1292, 1293, 1, 0, 0, 0, 1293, 1294, 6, 157, 10, 0, 1294, 330, 1, 0, 0, 0, 1295, 1296, 3, 57, 21, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1298, 6, 158, 10, 0, 1298, 332, 1, 0, 0, 0, 1299, 1300, 3, 59, 22, 0, 1300, 1301, 1, 0, 0, 0, 1301, 1302, 6, 159, 10, 0, 1302, 334, 1, 0, 0, 0, 1303, 1304, 3, 167, 76, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1306, 6, 160, 16, 0, 1306, 1307, 6, 160, 11, 0, 1307, 336, 1, 0, 0, 0, 1308, 1309, 5, 58, 0, 0, 1309, 338, 1, 0, 0, 0, 1310, 1316, 3, 73, 29, 0, 1311, 1316, 3, 63, 24, 0, 1312, 1316, 3, 103, 44, 0, 1313, 1316, 3, 65, 25, 0, 1314, 1316, 3, 79, 32, 0, 1315, 1310, 1, 0, 0, 0, 1315, 1311, 1, 0, 0, 0, 1315, 1312, 1, 0, 0, 0, 1315, 1313, 1, 0, 0, 0, 1315, 1314, 1, 0, 0, 0, 1316, 1317, 1, 0, 0, 0, 1317, 1315, 1, 0, 0, 0, 1317, 1318, 1, 0, 0, 0, 1318, 340, 1, 0, 0, 0, 1319, 1320, 3, 55, 20, 0, 1320, 1321, 1, 0, 0, 0, 1321, 1322, 6, 163, 10, 0, 1322, 342, 1, 0, 0, 0, 1323, 1324, 3, 57, 21, 0, 1324, 1325, 1, 0, 0, 0, 1325, 1326, 6, 164, 10, 0, 1326, 344, 1, 0, 0, 0, 1327, 1328, 3, 59, 22, 0, 1328, 1329, 1, 0, 0, 0, 1329, 1330, 6, 165, 10, 0, 1330, 346, 1, 0, 0, 0, 1331, 1332, 3, 61, 23, 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 6, 166, 15, 0, 1334, 1335, 6, 166, 11, 0, 1335, 348, 1, 0, 0, 0, 1336, 1337, 3, 337, 161, 0, 1337, 1338, 1, 0, 0, 0, 1338, 1339, 6, 167, 17, 0, 1339, 350, 1, 0, 0, 0, 1340, 1341, 3, 99, 42, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 168, 18, 0, 1343, 352, 1, 0, 0, 0, 1344, 1345, 3, 103, 44, 0, 1345, 1346, 1, 0, 0, 0, 1346, 1347, 6, 169, 22, 0, 1347, 354, 1, 0, 0, 0, 1348, 1349, 3, 267, 126, 0, 1349, 1350, 1, 0, 0, 0, 1350, 1351, 6, 170, 32, 0, 1351, 1352, 6, 170, 33, 0, 1352, 356, 1, 0, 0, 0, 1353, 1354, 3, 207, 96, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 171, 20, 0, 1356, 358, 1, 0, 0, 0, 1357, 1358, 3, 83, 34, 0, 1358, 1359, 1, 0, 0, 0, 1359, 1360, 6, 172, 21, 0, 1360, 360, 1, 0, 0, 0, 1361, 1362, 3, 55, 20, 0, 1362, 1363, 1, 0, 0, 0, 1363, 1364, 6, 173, 10, 0, 1364, 362, 1, 0, 0, 0, 1365, 1366, 3, 57, 21, 0, 1366, 1367, 1, 0, 0, 0, 1367, 1368, 6, 174, 10, 0, 1368, 364, 1, 0, 0, 0, 1369, 1370, 3, 59, 22, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 175, 10, 0, 1372, 366, 1, 0, 0, 0, 1373, 1374, 3, 61, 23, 0, 1374, 1375, 1, 0, 0, 0, 1375, 1376, 6, 176, 15, 0, 1376, 1377, 6, 176, 11, 0, 1377, 1378, 6, 176, 11, 0, 1378, 368, 1, 0, 0, 0, 1379, 1380, 3, 99, 42, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 177, 18, 0, 1382, 370, 1, 0, 0, 0, 1383, 1384, 3, 103, 44, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 178, 22, 0, 1386, 372, 1, 0, 0, 0, 1387, 1388, 3, 233, 109, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 179, 25, 0, 1390, 374, 1, 0, 0, 0, 1391, 1392, 3, 55, 20, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 180, 10, 0, 1394, 376, 1, 0, 0, 0, 1395, 1396, 3, 57, 21, 0, 1396, 1397, 1, 0, 0, 0, 1397, 1398, 6, 181, 10, 0, 1398, 378, 1, 0, 0, 0, 1399, 1400, 3, 59, 22, 0, 1400, 1401, 1, 0, 0, 0, 1401, 1402, 6, 182, 10, 0, 1402, 380, 1, 0, 0, 0, 1403, 1404, 3, 61, 23, 0, 1404, 1405, 1, 0, 0, 0, 1405, 1406, 6, 183, 15, 0, 1406, 1407, 6, 183, 11, 0, 1407, 382, 1, 0, 0, 0, 1408, 1409, 3, 207, 96, 0, 1409, 1410, 1, 0, 0, 0, 1410, 1411, 6, 184, 20, 0, 1411, 1412, 6, 184, 11, 0, 1412, 1413, 6, 184, 34, 0, 1413, 384, 1, 0, 0, 0, 1414, 1415, 3, 83, 34, 0, 1415, 1416, 1, 0, 0, 0, 1416, 1417, 6, 185, 21, 0, 1417, 1418, 6, 185, 11, 0, 1418, 1419, 6, 185, 34, 0, 1419, 386, 1, 0, 0, 0, 1420, 1421, 3, 55, 20, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1423, 6, 186, 10, 0, 1423, 388, 1, 0, 0, 0, 1424, 1425, 3, 57, 21, 0, 1425, 1426, 1, 0, 0, 0, 1426, 1427, 6, 187, 10, 0, 1427, 390, 1, 0, 0, 0, 1428, 1429, 3, 59, 22, 0, 1429, 1430, 1, 0, 0, 0, 1430, 1431, 6, 188, 10, 0, 1431, 392, 1, 0, 0, 0, 1432, 1433, 3, 337, 161, 0, 1433, 1434, 1, 0, 0, 0, 1434, 1435, 6, 189, 17, 0, 1435, 1436, 6, 189, 11, 0, 1436, 1437, 6, 189, 9, 0, 1437, 394, 1, 0, 0, 0, 1438, 1439, 3, 99, 42, 0, 1439, 1440, 1, 0, 0, 0, 1440, 1441, 6, 190, 18, 0, 1441, 1442, 6, 190, 11, 0, 1442, 1443, 6, 190, 9, 0, 1443, 396, 1, 0, 0, 0, 1444, 1445, 3, 55, 20, 0, 1445, 1446, 1, 0, 0, 0, 1446, 1447, 6, 191, 10, 0, 1447, 398, 1, 0, 0, 0, 1448, 1449, 3, 57, 21, 0, 1449, 1450, 1, 0, 0, 0, 1450, 1451, 6, 192, 10, 0, 1451, 400, 1, 0, 0, 0, 1452, 1453, 3, 59, 22, 0, 1453, 1454, 1, 0, 0, 0, 1454, 1455, 6, 193, 10, 0, 1455, 402, 1, 0, 0, 0, 1456, 1457, 3, 173, 79, 0, 1457, 1458, 1, 0, 0, 0, 1458, 1459, 6, 194, 11, 0, 1459, 1460, 6, 194, 0, 0, 1460, 1461, 6, 194, 30, 0, 1461, 404, 1, 0, 0, 0, 1462, 1463, 3, 169, 77, 0, 1463, 1464, 1, 0, 0, 0, 1464, 1465, 6, 195, 11, 0, 1465, 1466, 6, 195, 0, 0, 1466, 1467, 6, 195, 31, 0, 1467, 406, 1, 0, 0, 0, 1468, 1469, 3, 89, 37, 0, 1469, 1470, 1, 0, 0, 0, 1470, 1471, 6, 196, 11, 0, 1471, 1472, 6, 196, 0, 0, 1472, 1473, 6, 196, 35, 0, 1473, 408, 1, 0, 0, 0, 1474, 1475, 3, 61, 23, 0, 1475, 1476, 1, 0, 0, 0, 1476, 1477, 6, 197, 15, 0, 1477, 1478, 6, 197, 11, 0, 1478, 410, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 579, 589, 593, 596, 605, 607, 618, 637, 642, 651, 658, 663, 665, 676, 684, 687, 689, 694, 699, 705, 712, 717, 723, 726, 734, 738, 870, 875, 882, 884, 900, 905, 910, 912, 918, 995, 1000, 1049, 1053, 1058, 1063, 1068, 1070, 1074, 1076, 1163, 1167, 1172, 1315, 1317, 36, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 65, 0, 5, 0, 0, 7, 24, 0, 7, 66, 0, 7, 104, 0, 7, 33, 0, 7, 31, 0, 7, 76, 0, 7, 25, 0, 7, 35, 0, 7, 47, 0, 7, 64, 0, 7, 80, 0, 5, 10, 0, 5, 7, 0, 7, 90, 0, 7, 89, 0, 7, 68, 0, 7, 67, 0, 7, 88, 0, 5, 12, 0, 5, 14, 0, 7, 28, 0] \ No newline at end of file +[4, 0, 119, 1484, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 4, 19, 580, 8, 19, 11, 19, 12, 19, 581, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 590, 8, 20, 10, 20, 12, 20, 593, 9, 20, 1, 20, 3, 20, 596, 8, 20, 1, 20, 3, 20, 599, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 5, 21, 608, 8, 21, 10, 21, 12, 21, 611, 9, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 4, 22, 619, 8, 22, 11, 22, 12, 22, 620, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 3, 29, 642, 8, 29, 1, 29, 4, 29, 645, 8, 29, 11, 29, 12, 29, 646, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 3, 32, 656, 8, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 3, 34, 663, 8, 34, 1, 35, 1, 35, 1, 35, 5, 35, 668, 8, 35, 10, 35, 12, 35, 671, 9, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 679, 8, 35, 10, 35, 12, 35, 682, 9, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 689, 8, 35, 1, 35, 3, 35, 692, 8, 35, 3, 35, 694, 8, 35, 1, 36, 4, 36, 697, 8, 36, 11, 36, 12, 36, 698, 1, 37, 4, 37, 702, 8, 37, 11, 37, 12, 37, 703, 1, 37, 1, 37, 5, 37, 708, 8, 37, 10, 37, 12, 37, 711, 9, 37, 1, 37, 1, 37, 4, 37, 715, 8, 37, 11, 37, 12, 37, 716, 1, 37, 4, 37, 720, 8, 37, 11, 37, 12, 37, 721, 1, 37, 1, 37, 5, 37, 726, 8, 37, 10, 37, 12, 37, 729, 9, 37, 3, 37, 731, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 4, 37, 737, 8, 37, 11, 37, 12, 37, 738, 1, 37, 1, 37, 3, 37, 743, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, 75, 874, 8, 75, 1, 75, 5, 75, 877, 8, 75, 10, 75, 12, 75, 880, 9, 75, 1, 75, 1, 75, 4, 75, 884, 8, 75, 11, 75, 12, 75, 885, 3, 75, 888, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 5, 78, 902, 8, 78, 10, 78, 12, 78, 905, 9, 78, 1, 78, 1, 78, 3, 78, 909, 8, 78, 1, 78, 4, 78, 912, 8, 78, 11, 78, 12, 78, 913, 3, 78, 916, 8, 78, 1, 79, 1, 79, 4, 79, 920, 8, 79, 11, 79, 12, 79, 921, 1, 79, 1, 79, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 3, 96, 999, 8, 96, 1, 97, 4, 97, 1002, 8, 97, 11, 97, 12, 97, 1003, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 3, 108, 1053, 8, 108, 1, 109, 1, 109, 3, 109, 1057, 8, 109, 1, 109, 5, 109, 1060, 8, 109, 10, 109, 12, 109, 1063, 9, 109, 1, 109, 1, 109, 3, 109, 1067, 8, 109, 1, 109, 4, 109, 1070, 8, 109, 11, 109, 12, 109, 1071, 3, 109, 1074, 8, 109, 1, 110, 1, 110, 4, 110, 1078, 8, 110, 11, 110, 12, 110, 1079, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 130, 4, 130, 1165, 8, 130, 11, 130, 12, 130, 1166, 1, 130, 1, 130, 3, 130, 1171, 8, 130, 1, 130, 4, 130, 1174, 8, 130, 11, 130, 12, 130, 1175, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 163, 4, 163, 1321, 8, 163, 11, 163, 12, 163, 1322, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 197, 1, 197, 1, 198, 1, 198, 1, 198, 1, 198, 1, 198, 2, 609, 680, 0, 199, 15, 1, 17, 2, 19, 3, 21, 4, 23, 5, 25, 6, 27, 7, 29, 8, 31, 9, 33, 10, 35, 11, 37, 12, 39, 13, 41, 14, 43, 15, 45, 16, 47, 17, 49, 18, 51, 19, 53, 20, 55, 21, 57, 22, 59, 23, 61, 24, 63, 25, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 0, 81, 0, 83, 0, 85, 26, 87, 27, 89, 28, 91, 29, 93, 30, 95, 31, 97, 32, 99, 33, 101, 34, 103, 35, 105, 36, 107, 37, 109, 38, 111, 39, 113, 40, 115, 41, 117, 42, 119, 43, 121, 44, 123, 45, 125, 46, 127, 47, 129, 48, 131, 49, 133, 50, 135, 51, 137, 52, 139, 53, 141, 54, 143, 55, 145, 56, 147, 57, 149, 58, 151, 59, 153, 60, 155, 61, 157, 62, 159, 63, 161, 0, 163, 0, 165, 64, 167, 65, 169, 66, 171, 67, 173, 0, 175, 68, 177, 69, 179, 70, 181, 71, 183, 0, 185, 0, 187, 72, 189, 73, 191, 74, 193, 0, 195, 0, 197, 0, 199, 0, 201, 0, 203, 0, 205, 75, 207, 0, 209, 76, 211, 0, 213, 0, 215, 77, 217, 78, 219, 79, 221, 0, 223, 0, 225, 0, 227, 0, 229, 0, 231, 0, 233, 0, 235, 80, 237, 81, 239, 82, 241, 83, 243, 0, 245, 0, 247, 0, 249, 0, 251, 0, 253, 0, 255, 84, 257, 0, 259, 85, 261, 86, 263, 87, 265, 0, 267, 0, 269, 88, 271, 89, 273, 0, 275, 90, 277, 0, 279, 91, 281, 92, 283, 93, 285, 0, 287, 0, 289, 0, 291, 0, 293, 0, 295, 0, 297, 0, 299, 0, 301, 0, 303, 94, 305, 95, 307, 96, 309, 0, 311, 0, 313, 0, 315, 0, 317, 0, 319, 0, 321, 97, 323, 98, 325, 99, 327, 0, 329, 100, 331, 101, 333, 102, 335, 103, 337, 0, 339, 0, 341, 104, 343, 105, 345, 106, 347, 107, 349, 0, 351, 0, 353, 0, 355, 0, 357, 0, 359, 0, 361, 0, 363, 108, 365, 109, 367, 110, 369, 0, 371, 0, 373, 0, 375, 0, 377, 111, 379, 112, 381, 113, 383, 0, 385, 0, 387, 0, 389, 114, 391, 115, 393, 116, 395, 0, 397, 0, 399, 117, 401, 118, 403, 119, 405, 0, 407, 0, 409, 0, 411, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1512, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 1, 63, 1, 0, 0, 0, 1, 85, 1, 0, 0, 0, 1, 87, 1, 0, 0, 0, 1, 89, 1, 0, 0, 0, 1, 91, 1, 0, 0, 0, 1, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0, 1, 97, 1, 0, 0, 0, 1, 99, 1, 0, 0, 0, 1, 101, 1, 0, 0, 0, 1, 103, 1, 0, 0, 0, 1, 105, 1, 0, 0, 0, 1, 107, 1, 0, 0, 0, 1, 109, 1, 0, 0, 0, 1, 111, 1, 0, 0, 0, 1, 113, 1, 0, 0, 0, 1, 115, 1, 0, 0, 0, 1, 117, 1, 0, 0, 0, 1, 119, 1, 0, 0, 0, 1, 121, 1, 0, 0, 0, 1, 123, 1, 0, 0, 0, 1, 125, 1, 0, 0, 0, 1, 127, 1, 0, 0, 0, 1, 129, 1, 0, 0, 0, 1, 131, 1, 0, 0, 0, 1, 133, 1, 0, 0, 0, 1, 135, 1, 0, 0, 0, 1, 137, 1, 0, 0, 0, 1, 139, 1, 0, 0, 0, 1, 141, 1, 0, 0, 0, 1, 143, 1, 0, 0, 0, 1, 145, 1, 0, 0, 0, 1, 147, 1, 0, 0, 0, 1, 149, 1, 0, 0, 0, 1, 151, 1, 0, 0, 0, 1, 153, 1, 0, 0, 0, 1, 155, 1, 0, 0, 0, 1, 157, 1, 0, 0, 0, 1, 159, 1, 0, 0, 0, 1, 161, 1, 0, 0, 0, 1, 163, 1, 0, 0, 0, 1, 165, 1, 0, 0, 0, 1, 167, 1, 0, 0, 0, 1, 169, 1, 0, 0, 0, 1, 171, 1, 0, 0, 0, 1, 175, 1, 0, 0, 0, 1, 177, 1, 0, 0, 0, 1, 179, 1, 0, 0, 0, 1, 181, 1, 0, 0, 0, 2, 183, 1, 0, 0, 0, 2, 185, 1, 0, 0, 0, 2, 187, 1, 0, 0, 0, 2, 189, 1, 0, 0, 0, 2, 191, 1, 0, 0, 0, 3, 193, 1, 0, 0, 0, 3, 195, 1, 0, 0, 0, 3, 197, 1, 0, 0, 0, 3, 199, 1, 0, 0, 0, 3, 201, 1, 0, 0, 0, 3, 203, 1, 0, 0, 0, 3, 205, 1, 0, 0, 0, 3, 209, 1, 0, 0, 0, 3, 211, 1, 0, 0, 0, 3, 213, 1, 0, 0, 0, 3, 215, 1, 0, 0, 0, 3, 217, 1, 0, 0, 0, 3, 219, 1, 0, 0, 0, 4, 221, 1, 0, 0, 0, 4, 223, 1, 0, 0, 0, 4, 225, 1, 0, 0, 0, 4, 227, 1, 0, 0, 0, 4, 229, 1, 0, 0, 0, 4, 235, 1, 0, 0, 0, 4, 237, 1, 0, 0, 0, 4, 239, 1, 0, 0, 0, 4, 241, 1, 0, 0, 0, 5, 243, 1, 0, 0, 0, 5, 245, 1, 0, 0, 0, 5, 247, 1, 0, 0, 0, 5, 249, 1, 0, 0, 0, 5, 251, 1, 0, 0, 0, 5, 253, 1, 0, 0, 0, 5, 255, 1, 0, 0, 0, 5, 257, 1, 0, 0, 0, 5, 259, 1, 0, 0, 0, 5, 261, 1, 0, 0, 0, 5, 263, 1, 0, 0, 0, 6, 265, 1, 0, 0, 0, 6, 267, 1, 0, 0, 0, 6, 269, 1, 0, 0, 0, 6, 271, 1, 0, 0, 0, 6, 275, 1, 0, 0, 0, 6, 277, 1, 0, 0, 0, 6, 279, 1, 0, 0, 0, 6, 281, 1, 0, 0, 0, 6, 283, 1, 0, 0, 0, 7, 285, 1, 0, 0, 0, 7, 287, 1, 0, 0, 0, 7, 289, 1, 0, 0, 0, 7, 291, 1, 0, 0, 0, 7, 293, 1, 0, 0, 0, 7, 295, 1, 0, 0, 0, 7, 297, 1, 0, 0, 0, 7, 299, 1, 0, 0, 0, 7, 301, 1, 0, 0, 0, 7, 303, 1, 0, 0, 0, 7, 305, 1, 0, 0, 0, 7, 307, 1, 0, 0, 0, 8, 309, 1, 0, 0, 0, 8, 311, 1, 0, 0, 0, 8, 313, 1, 0, 0, 0, 8, 315, 1, 0, 0, 0, 8, 317, 1, 0, 0, 0, 8, 319, 1, 0, 0, 0, 8, 321, 1, 0, 0, 0, 8, 323, 1, 0, 0, 0, 8, 325, 1, 0, 0, 0, 9, 327, 1, 0, 0, 0, 9, 329, 1, 0, 0, 0, 9, 331, 1, 0, 0, 0, 9, 333, 1, 0, 0, 0, 9, 335, 1, 0, 0, 0, 10, 337, 1, 0, 0, 0, 10, 339, 1, 0, 0, 0, 10, 341, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 10, 345, 1, 0, 0, 0, 10, 347, 1, 0, 0, 0, 11, 349, 1, 0, 0, 0, 11, 351, 1, 0, 0, 0, 11, 353, 1, 0, 0, 0, 11, 355, 1, 0, 0, 0, 11, 357, 1, 0, 0, 0, 11, 359, 1, 0, 0, 0, 11, 361, 1, 0, 0, 0, 11, 363, 1, 0, 0, 0, 11, 365, 1, 0, 0, 0, 11, 367, 1, 0, 0, 0, 12, 369, 1, 0, 0, 0, 12, 371, 1, 0, 0, 0, 12, 373, 1, 0, 0, 0, 12, 375, 1, 0, 0, 0, 12, 377, 1, 0, 0, 0, 12, 379, 1, 0, 0, 0, 12, 381, 1, 0, 0, 0, 13, 383, 1, 0, 0, 0, 13, 385, 1, 0, 0, 0, 13, 387, 1, 0, 0, 0, 13, 389, 1, 0, 0, 0, 13, 391, 1, 0, 0, 0, 13, 393, 1, 0, 0, 0, 14, 395, 1, 0, 0, 0, 14, 397, 1, 0, 0, 0, 14, 399, 1, 0, 0, 0, 14, 401, 1, 0, 0, 0, 14, 403, 1, 0, 0, 0, 14, 405, 1, 0, 0, 0, 14, 407, 1, 0, 0, 0, 14, 409, 1, 0, 0, 0, 14, 411, 1, 0, 0, 0, 15, 413, 1, 0, 0, 0, 17, 423, 1, 0, 0, 0, 19, 430, 1, 0, 0, 0, 21, 439, 1, 0, 0, 0, 23, 446, 1, 0, 0, 0, 25, 456, 1, 0, 0, 0, 27, 463, 1, 0, 0, 0, 29, 470, 1, 0, 0, 0, 31, 477, 1, 0, 0, 0, 33, 485, 1, 0, 0, 0, 35, 497, 1, 0, 0, 0, 37, 506, 1, 0, 0, 0, 39, 512, 1, 0, 0, 0, 41, 519, 1, 0, 0, 0, 43, 526, 1, 0, 0, 0, 45, 534, 1, 0, 0, 0, 47, 542, 1, 0, 0, 0, 49, 557, 1, 0, 0, 0, 51, 567, 1, 0, 0, 0, 53, 579, 1, 0, 0, 0, 55, 585, 1, 0, 0, 0, 57, 602, 1, 0, 0, 0, 59, 618, 1, 0, 0, 0, 61, 624, 1, 0, 0, 0, 63, 626, 1, 0, 0, 0, 65, 630, 1, 0, 0, 0, 67, 632, 1, 0, 0, 0, 69, 634, 1, 0, 0, 0, 71, 637, 1, 0, 0, 0, 73, 639, 1, 0, 0, 0, 75, 648, 1, 0, 0, 0, 77, 650, 1, 0, 0, 0, 79, 655, 1, 0, 0, 0, 81, 657, 1, 0, 0, 0, 83, 662, 1, 0, 0, 0, 85, 693, 1, 0, 0, 0, 87, 696, 1, 0, 0, 0, 89, 742, 1, 0, 0, 0, 91, 744, 1, 0, 0, 0, 93, 747, 1, 0, 0, 0, 95, 751, 1, 0, 0, 0, 97, 755, 1, 0, 0, 0, 99, 757, 1, 0, 0, 0, 101, 760, 1, 0, 0, 0, 103, 762, 1, 0, 0, 0, 105, 767, 1, 0, 0, 0, 107, 769, 1, 0, 0, 0, 109, 775, 1, 0, 0, 0, 111, 781, 1, 0, 0, 0, 113, 784, 1, 0, 0, 0, 115, 787, 1, 0, 0, 0, 117, 792, 1, 0, 0, 0, 119, 797, 1, 0, 0, 0, 121, 799, 1, 0, 0, 0, 123, 803, 1, 0, 0, 0, 125, 808, 1, 0, 0, 0, 127, 814, 1, 0, 0, 0, 129, 817, 1, 0, 0, 0, 131, 819, 1, 0, 0, 0, 133, 825, 1, 0, 0, 0, 135, 827, 1, 0, 0, 0, 137, 832, 1, 0, 0, 0, 139, 835, 1, 0, 0, 0, 141, 838, 1, 0, 0, 0, 143, 841, 1, 0, 0, 0, 145, 843, 1, 0, 0, 0, 147, 846, 1, 0, 0, 0, 149, 848, 1, 0, 0, 0, 151, 851, 1, 0, 0, 0, 153, 853, 1, 0, 0, 0, 155, 855, 1, 0, 0, 0, 157, 857, 1, 0, 0, 0, 159, 859, 1, 0, 0, 0, 161, 861, 1, 0, 0, 0, 163, 866, 1, 0, 0, 0, 165, 887, 1, 0, 0, 0, 167, 889, 1, 0, 0, 0, 169, 894, 1, 0, 0, 0, 171, 915, 1, 0, 0, 0, 173, 917, 1, 0, 0, 0, 175, 925, 1, 0, 0, 0, 177, 927, 1, 0, 0, 0, 179, 931, 1, 0, 0, 0, 181, 935, 1, 0, 0, 0, 183, 939, 1, 0, 0, 0, 185, 944, 1, 0, 0, 0, 187, 949, 1, 0, 0, 0, 189, 953, 1, 0, 0, 0, 191, 957, 1, 0, 0, 0, 193, 961, 1, 0, 0, 0, 195, 966, 1, 0, 0, 0, 197, 970, 1, 0, 0, 0, 199, 974, 1, 0, 0, 0, 201, 978, 1, 0, 0, 0, 203, 982, 1, 0, 0, 0, 205, 986, 1, 0, 0, 0, 207, 998, 1, 0, 0, 0, 209, 1001, 1, 0, 0, 0, 211, 1005, 1, 0, 0, 0, 213, 1009, 1, 0, 0, 0, 215, 1013, 1, 0, 0, 0, 217, 1017, 1, 0, 0, 0, 219, 1021, 1, 0, 0, 0, 221, 1025, 1, 0, 0, 0, 223, 1030, 1, 0, 0, 0, 225, 1034, 1, 0, 0, 0, 227, 1038, 1, 0, 0, 0, 229, 1043, 1, 0, 0, 0, 231, 1052, 1, 0, 0, 0, 233, 1073, 1, 0, 0, 0, 235, 1077, 1, 0, 0, 0, 237, 1081, 1, 0, 0, 0, 239, 1085, 1, 0, 0, 0, 241, 1089, 1, 0, 0, 0, 243, 1093, 1, 0, 0, 0, 245, 1098, 1, 0, 0, 0, 247, 1102, 1, 0, 0, 0, 249, 1106, 1, 0, 0, 0, 251, 1110, 1, 0, 0, 0, 253, 1115, 1, 0, 0, 0, 255, 1120, 1, 0, 0, 0, 257, 1123, 1, 0, 0, 0, 259, 1127, 1, 0, 0, 0, 261, 1131, 1, 0, 0, 0, 263, 1135, 1, 0, 0, 0, 265, 1139, 1, 0, 0, 0, 267, 1144, 1, 0, 0, 0, 269, 1149, 1, 0, 0, 0, 271, 1154, 1, 0, 0, 0, 273, 1161, 1, 0, 0, 0, 275, 1170, 1, 0, 0, 0, 277, 1177, 1, 0, 0, 0, 279, 1181, 1, 0, 0, 0, 281, 1185, 1, 0, 0, 0, 283, 1189, 1, 0, 0, 0, 285, 1193, 1, 0, 0, 0, 287, 1199, 1, 0, 0, 0, 289, 1203, 1, 0, 0, 0, 291, 1207, 1, 0, 0, 0, 293, 1211, 1, 0, 0, 0, 295, 1215, 1, 0, 0, 0, 297, 1219, 1, 0, 0, 0, 299, 1223, 1, 0, 0, 0, 301, 1228, 1, 0, 0, 0, 303, 1233, 1, 0, 0, 0, 305, 1237, 1, 0, 0, 0, 307, 1241, 1, 0, 0, 0, 309, 1245, 1, 0, 0, 0, 311, 1250, 1, 0, 0, 0, 313, 1254, 1, 0, 0, 0, 315, 1259, 1, 0, 0, 0, 317, 1264, 1, 0, 0, 0, 319, 1268, 1, 0, 0, 0, 321, 1272, 1, 0, 0, 0, 323, 1276, 1, 0, 0, 0, 325, 1280, 1, 0, 0, 0, 327, 1284, 1, 0, 0, 0, 329, 1289, 1, 0, 0, 0, 331, 1294, 1, 0, 0, 0, 333, 1298, 1, 0, 0, 0, 335, 1302, 1, 0, 0, 0, 337, 1306, 1, 0, 0, 0, 339, 1311, 1, 0, 0, 0, 341, 1320, 1, 0, 0, 0, 343, 1324, 1, 0, 0, 0, 345, 1328, 1, 0, 0, 0, 347, 1332, 1, 0, 0, 0, 349, 1336, 1, 0, 0, 0, 351, 1341, 1, 0, 0, 0, 353, 1345, 1, 0, 0, 0, 355, 1349, 1, 0, 0, 0, 357, 1353, 1, 0, 0, 0, 359, 1358, 1, 0, 0, 0, 361, 1362, 1, 0, 0, 0, 363, 1366, 1, 0, 0, 0, 365, 1370, 1, 0, 0, 0, 367, 1374, 1, 0, 0, 0, 369, 1378, 1, 0, 0, 0, 371, 1384, 1, 0, 0, 0, 373, 1388, 1, 0, 0, 0, 375, 1392, 1, 0, 0, 0, 377, 1396, 1, 0, 0, 0, 379, 1400, 1, 0, 0, 0, 381, 1404, 1, 0, 0, 0, 383, 1408, 1, 0, 0, 0, 385, 1413, 1, 0, 0, 0, 387, 1419, 1, 0, 0, 0, 389, 1425, 1, 0, 0, 0, 391, 1429, 1, 0, 0, 0, 393, 1433, 1, 0, 0, 0, 395, 1437, 1, 0, 0, 0, 397, 1443, 1, 0, 0, 0, 399, 1449, 1, 0, 0, 0, 401, 1453, 1, 0, 0, 0, 403, 1457, 1, 0, 0, 0, 405, 1461, 1, 0, 0, 0, 407, 1467, 1, 0, 0, 0, 409, 1473, 1, 0, 0, 0, 411, 1479, 1, 0, 0, 0, 413, 414, 7, 0, 0, 0, 414, 415, 7, 1, 0, 0, 415, 416, 7, 2, 0, 0, 416, 417, 7, 2, 0, 0, 417, 418, 7, 3, 0, 0, 418, 419, 7, 4, 0, 0, 419, 420, 7, 5, 0, 0, 420, 421, 1, 0, 0, 0, 421, 422, 6, 0, 0, 0, 422, 16, 1, 0, 0, 0, 423, 424, 7, 0, 0, 0, 424, 425, 7, 6, 0, 0, 425, 426, 7, 7, 0, 0, 426, 427, 7, 8, 0, 0, 427, 428, 1, 0, 0, 0, 428, 429, 6, 1, 1, 0, 429, 18, 1, 0, 0, 0, 430, 431, 7, 3, 0, 0, 431, 432, 7, 9, 0, 0, 432, 433, 7, 6, 0, 0, 433, 434, 7, 1, 0, 0, 434, 435, 7, 4, 0, 0, 435, 436, 7, 10, 0, 0, 436, 437, 1, 0, 0, 0, 437, 438, 6, 2, 2, 0, 438, 20, 1, 0, 0, 0, 439, 440, 7, 3, 0, 0, 440, 441, 7, 11, 0, 0, 441, 442, 7, 12, 0, 0, 442, 443, 7, 13, 0, 0, 443, 444, 1, 0, 0, 0, 444, 445, 6, 3, 0, 0, 445, 22, 1, 0, 0, 0, 446, 447, 7, 3, 0, 0, 447, 448, 7, 14, 0, 0, 448, 449, 7, 8, 0, 0, 449, 450, 7, 13, 0, 0, 450, 451, 7, 12, 0, 0, 451, 452, 7, 1, 0, 0, 452, 453, 7, 9, 0, 0, 453, 454, 1, 0, 0, 0, 454, 455, 6, 4, 3, 0, 455, 24, 1, 0, 0, 0, 456, 457, 7, 15, 0, 0, 457, 458, 7, 6, 0, 0, 458, 459, 7, 7, 0, 0, 459, 460, 7, 16, 0, 0, 460, 461, 1, 0, 0, 0, 461, 462, 6, 5, 4, 0, 462, 26, 1, 0, 0, 0, 463, 464, 7, 17, 0, 0, 464, 465, 7, 6, 0, 0, 465, 466, 7, 7, 0, 0, 466, 467, 7, 18, 0, 0, 467, 468, 1, 0, 0, 0, 468, 469, 6, 6, 0, 0, 469, 28, 1, 0, 0, 0, 470, 471, 7, 18, 0, 0, 471, 472, 7, 3, 0, 0, 472, 473, 7, 3, 0, 0, 473, 474, 7, 8, 0, 0, 474, 475, 1, 0, 0, 0, 475, 476, 6, 7, 1, 0, 476, 30, 1, 0, 0, 0, 477, 478, 7, 13, 0, 0, 478, 479, 7, 1, 0, 0, 479, 480, 7, 16, 0, 0, 480, 481, 7, 1, 0, 0, 481, 482, 7, 5, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 6, 8, 0, 0, 484, 32, 1, 0, 0, 0, 485, 486, 7, 16, 0, 0, 486, 487, 7, 11, 0, 0, 487, 488, 5, 95, 0, 0, 488, 489, 7, 3, 0, 0, 489, 490, 7, 14, 0, 0, 490, 491, 7, 8, 0, 0, 491, 492, 7, 12, 0, 0, 492, 493, 7, 9, 0, 0, 493, 494, 7, 0, 0, 0, 494, 495, 1, 0, 0, 0, 495, 496, 6, 9, 5, 0, 496, 34, 1, 0, 0, 0, 497, 498, 7, 6, 0, 0, 498, 499, 7, 3, 0, 0, 499, 500, 7, 9, 0, 0, 500, 501, 7, 12, 0, 0, 501, 502, 7, 16, 0, 0, 502, 503, 7, 3, 0, 0, 503, 504, 1, 0, 0, 0, 504, 505, 6, 10, 6, 0, 505, 36, 1, 0, 0, 0, 506, 507, 7, 6, 0, 0, 507, 508, 7, 7, 0, 0, 508, 509, 7, 19, 0, 0, 509, 510, 1, 0, 0, 0, 510, 511, 6, 11, 0, 0, 511, 38, 1, 0, 0, 0, 512, 513, 7, 2, 0, 0, 513, 514, 7, 10, 0, 0, 514, 515, 7, 7, 0, 0, 515, 516, 7, 19, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 6, 12, 7, 0, 518, 40, 1, 0, 0, 0, 519, 520, 7, 2, 0, 0, 520, 521, 7, 7, 0, 0, 521, 522, 7, 6, 0, 0, 522, 523, 7, 5, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 6, 13, 0, 0, 525, 42, 1, 0, 0, 0, 526, 527, 7, 2, 0, 0, 527, 528, 7, 5, 0, 0, 528, 529, 7, 12, 0, 0, 529, 530, 7, 5, 0, 0, 530, 531, 7, 2, 0, 0, 531, 532, 1, 0, 0, 0, 532, 533, 6, 14, 0, 0, 533, 44, 1, 0, 0, 0, 534, 535, 7, 19, 0, 0, 535, 536, 7, 10, 0, 0, 536, 537, 7, 3, 0, 0, 537, 538, 7, 6, 0, 0, 538, 539, 7, 3, 0, 0, 539, 540, 1, 0, 0, 0, 540, 541, 6, 15, 0, 0, 541, 46, 1, 0, 0, 0, 542, 543, 4, 16, 0, 0, 543, 544, 7, 1, 0, 0, 544, 545, 7, 9, 0, 0, 545, 546, 7, 13, 0, 0, 546, 547, 7, 1, 0, 0, 547, 548, 7, 9, 0, 0, 548, 549, 7, 3, 0, 0, 549, 550, 7, 2, 0, 0, 550, 551, 7, 5, 0, 0, 551, 552, 7, 12, 0, 0, 552, 553, 7, 5, 0, 0, 553, 554, 7, 2, 0, 0, 554, 555, 1, 0, 0, 0, 555, 556, 6, 16, 0, 0, 556, 48, 1, 0, 0, 0, 557, 558, 4, 17, 1, 0, 558, 559, 7, 13, 0, 0, 559, 560, 7, 7, 0, 0, 560, 561, 7, 7, 0, 0, 561, 562, 7, 18, 0, 0, 562, 563, 7, 20, 0, 0, 563, 564, 7, 8, 0, 0, 564, 565, 1, 0, 0, 0, 565, 566, 6, 17, 8, 0, 566, 50, 1, 0, 0, 0, 567, 568, 4, 18, 2, 0, 568, 569, 7, 16, 0, 0, 569, 570, 7, 3, 0, 0, 570, 571, 7, 5, 0, 0, 571, 572, 7, 6, 0, 0, 572, 573, 7, 1, 0, 0, 573, 574, 7, 4, 0, 0, 574, 575, 7, 2, 0, 0, 575, 576, 1, 0, 0, 0, 576, 577, 6, 18, 9, 0, 577, 52, 1, 0, 0, 0, 578, 580, 8, 21, 0, 0, 579, 578, 1, 0, 0, 0, 580, 581, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 581, 582, 1, 0, 0, 0, 582, 583, 1, 0, 0, 0, 583, 584, 6, 19, 0, 0, 584, 54, 1, 0, 0, 0, 585, 586, 5, 47, 0, 0, 586, 587, 5, 47, 0, 0, 587, 591, 1, 0, 0, 0, 588, 590, 8, 22, 0, 0, 589, 588, 1, 0, 0, 0, 590, 593, 1, 0, 0, 0, 591, 589, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 595, 1, 0, 0, 0, 593, 591, 1, 0, 0, 0, 594, 596, 5, 13, 0, 0, 595, 594, 1, 0, 0, 0, 595, 596, 1, 0, 0, 0, 596, 598, 1, 0, 0, 0, 597, 599, 5, 10, 0, 0, 598, 597, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 600, 1, 0, 0, 0, 600, 601, 6, 20, 10, 0, 601, 56, 1, 0, 0, 0, 602, 603, 5, 47, 0, 0, 603, 604, 5, 42, 0, 0, 604, 609, 1, 0, 0, 0, 605, 608, 3, 57, 21, 0, 606, 608, 9, 0, 0, 0, 607, 605, 1, 0, 0, 0, 607, 606, 1, 0, 0, 0, 608, 611, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 612, 1, 0, 0, 0, 611, 609, 1, 0, 0, 0, 612, 613, 5, 42, 0, 0, 613, 614, 5, 47, 0, 0, 614, 615, 1, 0, 0, 0, 615, 616, 6, 21, 10, 0, 616, 58, 1, 0, 0, 0, 617, 619, 7, 23, 0, 0, 618, 617, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 618, 1, 0, 0, 0, 620, 621, 1, 0, 0, 0, 621, 622, 1, 0, 0, 0, 622, 623, 6, 22, 10, 0, 623, 60, 1, 0, 0, 0, 624, 625, 5, 58, 0, 0, 625, 62, 1, 0, 0, 0, 626, 627, 5, 124, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 6, 24, 11, 0, 629, 64, 1, 0, 0, 0, 630, 631, 7, 24, 0, 0, 631, 66, 1, 0, 0, 0, 632, 633, 7, 25, 0, 0, 633, 68, 1, 0, 0, 0, 634, 635, 5, 92, 0, 0, 635, 636, 7, 26, 0, 0, 636, 70, 1, 0, 0, 0, 637, 638, 8, 27, 0, 0, 638, 72, 1, 0, 0, 0, 639, 641, 7, 3, 0, 0, 640, 642, 7, 28, 0, 0, 641, 640, 1, 0, 0, 0, 641, 642, 1, 0, 0, 0, 642, 644, 1, 0, 0, 0, 643, 645, 3, 65, 25, 0, 644, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 644, 1, 0, 0, 0, 646, 647, 1, 0, 0, 0, 647, 74, 1, 0, 0, 0, 648, 649, 5, 64, 0, 0, 649, 76, 1, 0, 0, 0, 650, 651, 5, 96, 0, 0, 651, 78, 1, 0, 0, 0, 652, 656, 8, 29, 0, 0, 653, 654, 5, 96, 0, 0, 654, 656, 5, 96, 0, 0, 655, 652, 1, 0, 0, 0, 655, 653, 1, 0, 0, 0, 656, 80, 1, 0, 0, 0, 657, 658, 5, 95, 0, 0, 658, 82, 1, 0, 0, 0, 659, 663, 3, 67, 26, 0, 660, 663, 3, 65, 25, 0, 661, 663, 3, 81, 33, 0, 662, 659, 1, 0, 0, 0, 662, 660, 1, 0, 0, 0, 662, 661, 1, 0, 0, 0, 663, 84, 1, 0, 0, 0, 664, 669, 5, 34, 0, 0, 665, 668, 3, 69, 27, 0, 666, 668, 3, 71, 28, 0, 667, 665, 1, 0, 0, 0, 667, 666, 1, 0, 0, 0, 668, 671, 1, 0, 0, 0, 669, 667, 1, 0, 0, 0, 669, 670, 1, 0, 0, 0, 670, 672, 1, 0, 0, 0, 671, 669, 1, 0, 0, 0, 672, 694, 5, 34, 0, 0, 673, 674, 5, 34, 0, 0, 674, 675, 5, 34, 0, 0, 675, 676, 5, 34, 0, 0, 676, 680, 1, 0, 0, 0, 677, 679, 8, 22, 0, 0, 678, 677, 1, 0, 0, 0, 679, 682, 1, 0, 0, 0, 680, 681, 1, 0, 0, 0, 680, 678, 1, 0, 0, 0, 681, 683, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 684, 5, 34, 0, 0, 684, 685, 5, 34, 0, 0, 685, 686, 5, 34, 0, 0, 686, 688, 1, 0, 0, 0, 687, 689, 5, 34, 0, 0, 688, 687, 1, 0, 0, 0, 688, 689, 1, 0, 0, 0, 689, 691, 1, 0, 0, 0, 690, 692, 5, 34, 0, 0, 691, 690, 1, 0, 0, 0, 691, 692, 1, 0, 0, 0, 692, 694, 1, 0, 0, 0, 693, 664, 1, 0, 0, 0, 693, 673, 1, 0, 0, 0, 694, 86, 1, 0, 0, 0, 695, 697, 3, 65, 25, 0, 696, 695, 1, 0, 0, 0, 697, 698, 1, 0, 0, 0, 698, 696, 1, 0, 0, 0, 698, 699, 1, 0, 0, 0, 699, 88, 1, 0, 0, 0, 700, 702, 3, 65, 25, 0, 701, 700, 1, 0, 0, 0, 702, 703, 1, 0, 0, 0, 703, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 709, 3, 105, 45, 0, 706, 708, 3, 65, 25, 0, 707, 706, 1, 0, 0, 0, 708, 711, 1, 0, 0, 0, 709, 707, 1, 0, 0, 0, 709, 710, 1, 0, 0, 0, 710, 743, 1, 0, 0, 0, 711, 709, 1, 0, 0, 0, 712, 714, 3, 105, 45, 0, 713, 715, 3, 65, 25, 0, 714, 713, 1, 0, 0, 0, 715, 716, 1, 0, 0, 0, 716, 714, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 743, 1, 0, 0, 0, 718, 720, 3, 65, 25, 0, 719, 718, 1, 0, 0, 0, 720, 721, 1, 0, 0, 0, 721, 719, 1, 0, 0, 0, 721, 722, 1, 0, 0, 0, 722, 730, 1, 0, 0, 0, 723, 727, 3, 105, 45, 0, 724, 726, 3, 65, 25, 0, 725, 724, 1, 0, 0, 0, 726, 729, 1, 0, 0, 0, 727, 725, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 731, 1, 0, 0, 0, 729, 727, 1, 0, 0, 0, 730, 723, 1, 0, 0, 0, 730, 731, 1, 0, 0, 0, 731, 732, 1, 0, 0, 0, 732, 733, 3, 73, 29, 0, 733, 743, 1, 0, 0, 0, 734, 736, 3, 105, 45, 0, 735, 737, 3, 65, 25, 0, 736, 735, 1, 0, 0, 0, 737, 738, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 738, 739, 1, 0, 0, 0, 739, 740, 1, 0, 0, 0, 740, 741, 3, 73, 29, 0, 741, 743, 1, 0, 0, 0, 742, 701, 1, 0, 0, 0, 742, 712, 1, 0, 0, 0, 742, 719, 1, 0, 0, 0, 742, 734, 1, 0, 0, 0, 743, 90, 1, 0, 0, 0, 744, 745, 7, 30, 0, 0, 745, 746, 7, 31, 0, 0, 746, 92, 1, 0, 0, 0, 747, 748, 7, 12, 0, 0, 748, 749, 7, 9, 0, 0, 749, 750, 7, 0, 0, 0, 750, 94, 1, 0, 0, 0, 751, 752, 7, 12, 0, 0, 752, 753, 7, 2, 0, 0, 753, 754, 7, 4, 0, 0, 754, 96, 1, 0, 0, 0, 755, 756, 5, 61, 0, 0, 756, 98, 1, 0, 0, 0, 757, 758, 5, 58, 0, 0, 758, 759, 5, 58, 0, 0, 759, 100, 1, 0, 0, 0, 760, 761, 5, 44, 0, 0, 761, 102, 1, 0, 0, 0, 762, 763, 7, 0, 0, 0, 763, 764, 7, 3, 0, 0, 764, 765, 7, 2, 0, 0, 765, 766, 7, 4, 0, 0, 766, 104, 1, 0, 0, 0, 767, 768, 5, 46, 0, 0, 768, 106, 1, 0, 0, 0, 769, 770, 7, 15, 0, 0, 770, 771, 7, 12, 0, 0, 771, 772, 7, 13, 0, 0, 772, 773, 7, 2, 0, 0, 773, 774, 7, 3, 0, 0, 774, 108, 1, 0, 0, 0, 775, 776, 7, 15, 0, 0, 776, 777, 7, 1, 0, 0, 777, 778, 7, 6, 0, 0, 778, 779, 7, 2, 0, 0, 779, 780, 7, 5, 0, 0, 780, 110, 1, 0, 0, 0, 781, 782, 7, 1, 0, 0, 782, 783, 7, 9, 0, 0, 783, 112, 1, 0, 0, 0, 784, 785, 7, 1, 0, 0, 785, 786, 7, 2, 0, 0, 786, 114, 1, 0, 0, 0, 787, 788, 7, 13, 0, 0, 788, 789, 7, 12, 0, 0, 789, 790, 7, 2, 0, 0, 790, 791, 7, 5, 0, 0, 791, 116, 1, 0, 0, 0, 792, 793, 7, 13, 0, 0, 793, 794, 7, 1, 0, 0, 794, 795, 7, 18, 0, 0, 795, 796, 7, 3, 0, 0, 796, 118, 1, 0, 0, 0, 797, 798, 5, 40, 0, 0, 798, 120, 1, 0, 0, 0, 799, 800, 7, 9, 0, 0, 800, 801, 7, 7, 0, 0, 801, 802, 7, 5, 0, 0, 802, 122, 1, 0, 0, 0, 803, 804, 7, 9, 0, 0, 804, 805, 7, 20, 0, 0, 805, 806, 7, 13, 0, 0, 806, 807, 7, 13, 0, 0, 807, 124, 1, 0, 0, 0, 808, 809, 7, 9, 0, 0, 809, 810, 7, 20, 0, 0, 810, 811, 7, 13, 0, 0, 811, 812, 7, 13, 0, 0, 812, 813, 7, 2, 0, 0, 813, 126, 1, 0, 0, 0, 814, 815, 7, 7, 0, 0, 815, 816, 7, 6, 0, 0, 816, 128, 1, 0, 0, 0, 817, 818, 5, 63, 0, 0, 818, 130, 1, 0, 0, 0, 819, 820, 7, 6, 0, 0, 820, 821, 7, 13, 0, 0, 821, 822, 7, 1, 0, 0, 822, 823, 7, 18, 0, 0, 823, 824, 7, 3, 0, 0, 824, 132, 1, 0, 0, 0, 825, 826, 5, 41, 0, 0, 826, 134, 1, 0, 0, 0, 827, 828, 7, 5, 0, 0, 828, 829, 7, 6, 0, 0, 829, 830, 7, 20, 0, 0, 830, 831, 7, 3, 0, 0, 831, 136, 1, 0, 0, 0, 832, 833, 5, 61, 0, 0, 833, 834, 5, 61, 0, 0, 834, 138, 1, 0, 0, 0, 835, 836, 5, 61, 0, 0, 836, 837, 5, 126, 0, 0, 837, 140, 1, 0, 0, 0, 838, 839, 5, 33, 0, 0, 839, 840, 5, 61, 0, 0, 840, 142, 1, 0, 0, 0, 841, 842, 5, 60, 0, 0, 842, 144, 1, 0, 0, 0, 843, 844, 5, 60, 0, 0, 844, 845, 5, 61, 0, 0, 845, 146, 1, 0, 0, 0, 846, 847, 5, 62, 0, 0, 847, 148, 1, 0, 0, 0, 848, 849, 5, 62, 0, 0, 849, 850, 5, 61, 0, 0, 850, 150, 1, 0, 0, 0, 851, 852, 5, 43, 0, 0, 852, 152, 1, 0, 0, 0, 853, 854, 5, 45, 0, 0, 854, 154, 1, 0, 0, 0, 855, 856, 5, 42, 0, 0, 856, 156, 1, 0, 0, 0, 857, 858, 5, 47, 0, 0, 858, 158, 1, 0, 0, 0, 859, 860, 5, 37, 0, 0, 860, 160, 1, 0, 0, 0, 861, 862, 4, 73, 3, 0, 862, 863, 3, 61, 23, 0, 863, 864, 1, 0, 0, 0, 864, 865, 6, 73, 12, 0, 865, 162, 1, 0, 0, 0, 866, 867, 3, 45, 15, 0, 867, 868, 1, 0, 0, 0, 868, 869, 6, 74, 13, 0, 869, 164, 1, 0, 0, 0, 870, 873, 3, 129, 57, 0, 871, 874, 3, 67, 26, 0, 872, 874, 3, 81, 33, 0, 873, 871, 1, 0, 0, 0, 873, 872, 1, 0, 0, 0, 874, 878, 1, 0, 0, 0, 875, 877, 3, 83, 34, 0, 876, 875, 1, 0, 0, 0, 877, 880, 1, 0, 0, 0, 878, 876, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 888, 1, 0, 0, 0, 880, 878, 1, 0, 0, 0, 881, 883, 3, 129, 57, 0, 882, 884, 3, 65, 25, 0, 883, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 883, 1, 0, 0, 0, 885, 886, 1, 0, 0, 0, 886, 888, 1, 0, 0, 0, 887, 870, 1, 0, 0, 0, 887, 881, 1, 0, 0, 0, 888, 166, 1, 0, 0, 0, 889, 890, 5, 91, 0, 0, 890, 891, 1, 0, 0, 0, 891, 892, 6, 76, 0, 0, 892, 893, 6, 76, 0, 0, 893, 168, 1, 0, 0, 0, 894, 895, 5, 93, 0, 0, 895, 896, 1, 0, 0, 0, 896, 897, 6, 77, 11, 0, 897, 898, 6, 77, 11, 0, 898, 170, 1, 0, 0, 0, 899, 903, 3, 67, 26, 0, 900, 902, 3, 83, 34, 0, 901, 900, 1, 0, 0, 0, 902, 905, 1, 0, 0, 0, 903, 901, 1, 0, 0, 0, 903, 904, 1, 0, 0, 0, 904, 916, 1, 0, 0, 0, 905, 903, 1, 0, 0, 0, 906, 909, 3, 81, 33, 0, 907, 909, 3, 75, 30, 0, 908, 906, 1, 0, 0, 0, 908, 907, 1, 0, 0, 0, 909, 911, 1, 0, 0, 0, 910, 912, 3, 83, 34, 0, 911, 910, 1, 0, 0, 0, 912, 913, 1, 0, 0, 0, 913, 911, 1, 0, 0, 0, 913, 914, 1, 0, 0, 0, 914, 916, 1, 0, 0, 0, 915, 899, 1, 0, 0, 0, 915, 908, 1, 0, 0, 0, 916, 172, 1, 0, 0, 0, 917, 919, 3, 77, 31, 0, 918, 920, 3, 79, 32, 0, 919, 918, 1, 0, 0, 0, 920, 921, 1, 0, 0, 0, 921, 919, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 923, 1, 0, 0, 0, 923, 924, 3, 77, 31, 0, 924, 174, 1, 0, 0, 0, 925, 926, 3, 173, 79, 0, 926, 176, 1, 0, 0, 0, 927, 928, 3, 55, 20, 0, 928, 929, 1, 0, 0, 0, 929, 930, 6, 81, 10, 0, 930, 178, 1, 0, 0, 0, 931, 932, 3, 57, 21, 0, 932, 933, 1, 0, 0, 0, 933, 934, 6, 82, 10, 0, 934, 180, 1, 0, 0, 0, 935, 936, 3, 59, 22, 0, 936, 937, 1, 0, 0, 0, 937, 938, 6, 83, 10, 0, 938, 182, 1, 0, 0, 0, 939, 940, 3, 167, 76, 0, 940, 941, 1, 0, 0, 0, 941, 942, 6, 84, 14, 0, 942, 943, 6, 84, 15, 0, 943, 184, 1, 0, 0, 0, 944, 945, 3, 63, 24, 0, 945, 946, 1, 0, 0, 0, 946, 947, 6, 85, 16, 0, 947, 948, 6, 85, 11, 0, 948, 186, 1, 0, 0, 0, 949, 950, 3, 59, 22, 0, 950, 951, 1, 0, 0, 0, 951, 952, 6, 86, 10, 0, 952, 188, 1, 0, 0, 0, 953, 954, 3, 55, 20, 0, 954, 955, 1, 0, 0, 0, 955, 956, 6, 87, 10, 0, 956, 190, 1, 0, 0, 0, 957, 958, 3, 57, 21, 0, 958, 959, 1, 0, 0, 0, 959, 960, 6, 88, 10, 0, 960, 192, 1, 0, 0, 0, 961, 962, 3, 63, 24, 0, 962, 963, 1, 0, 0, 0, 963, 964, 6, 89, 16, 0, 964, 965, 6, 89, 11, 0, 965, 194, 1, 0, 0, 0, 966, 967, 3, 167, 76, 0, 967, 968, 1, 0, 0, 0, 968, 969, 6, 90, 14, 0, 969, 196, 1, 0, 0, 0, 970, 971, 3, 169, 77, 0, 971, 972, 1, 0, 0, 0, 972, 973, 6, 91, 17, 0, 973, 198, 1, 0, 0, 0, 974, 975, 3, 61, 23, 0, 975, 976, 1, 0, 0, 0, 976, 977, 6, 92, 12, 0, 977, 200, 1, 0, 0, 0, 978, 979, 3, 101, 43, 0, 979, 980, 1, 0, 0, 0, 980, 981, 6, 93, 18, 0, 981, 202, 1, 0, 0, 0, 982, 983, 3, 97, 41, 0, 983, 984, 1, 0, 0, 0, 984, 985, 6, 94, 19, 0, 985, 204, 1, 0, 0, 0, 986, 987, 7, 16, 0, 0, 987, 988, 7, 3, 0, 0, 988, 989, 7, 5, 0, 0, 989, 990, 7, 12, 0, 0, 990, 991, 7, 0, 0, 0, 991, 992, 7, 12, 0, 0, 992, 993, 7, 5, 0, 0, 993, 994, 7, 12, 0, 0, 994, 206, 1, 0, 0, 0, 995, 999, 8, 32, 0, 0, 996, 997, 5, 47, 0, 0, 997, 999, 8, 33, 0, 0, 998, 995, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 999, 208, 1, 0, 0, 0, 1000, 1002, 3, 207, 96, 0, 1001, 1000, 1, 0, 0, 0, 1002, 1003, 1, 0, 0, 0, 1003, 1001, 1, 0, 0, 0, 1003, 1004, 1, 0, 0, 0, 1004, 210, 1, 0, 0, 0, 1005, 1006, 3, 209, 97, 0, 1006, 1007, 1, 0, 0, 0, 1007, 1008, 6, 98, 20, 0, 1008, 212, 1, 0, 0, 0, 1009, 1010, 3, 85, 35, 0, 1010, 1011, 1, 0, 0, 0, 1011, 1012, 6, 99, 21, 0, 1012, 214, 1, 0, 0, 0, 1013, 1014, 3, 55, 20, 0, 1014, 1015, 1, 0, 0, 0, 1015, 1016, 6, 100, 10, 0, 1016, 216, 1, 0, 0, 0, 1017, 1018, 3, 57, 21, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1020, 6, 101, 10, 0, 1020, 218, 1, 0, 0, 0, 1021, 1022, 3, 59, 22, 0, 1022, 1023, 1, 0, 0, 0, 1023, 1024, 6, 102, 10, 0, 1024, 220, 1, 0, 0, 0, 1025, 1026, 3, 63, 24, 0, 1026, 1027, 1, 0, 0, 0, 1027, 1028, 6, 103, 16, 0, 1028, 1029, 6, 103, 11, 0, 1029, 222, 1, 0, 0, 0, 1030, 1031, 3, 105, 45, 0, 1031, 1032, 1, 0, 0, 0, 1032, 1033, 6, 104, 22, 0, 1033, 224, 1, 0, 0, 0, 1034, 1035, 3, 101, 43, 0, 1035, 1036, 1, 0, 0, 0, 1036, 1037, 6, 105, 18, 0, 1037, 226, 1, 0, 0, 0, 1038, 1039, 4, 106, 4, 0, 1039, 1040, 3, 129, 57, 0, 1040, 1041, 1, 0, 0, 0, 1041, 1042, 6, 106, 23, 0, 1042, 228, 1, 0, 0, 0, 1043, 1044, 4, 107, 5, 0, 1044, 1045, 3, 165, 75, 0, 1045, 1046, 1, 0, 0, 0, 1046, 1047, 6, 107, 24, 0, 1047, 230, 1, 0, 0, 0, 1048, 1053, 3, 67, 26, 0, 1049, 1053, 3, 65, 25, 0, 1050, 1053, 3, 81, 33, 0, 1051, 1053, 3, 155, 70, 0, 1052, 1048, 1, 0, 0, 0, 1052, 1049, 1, 0, 0, 0, 1052, 1050, 1, 0, 0, 0, 1052, 1051, 1, 0, 0, 0, 1053, 232, 1, 0, 0, 0, 1054, 1057, 3, 67, 26, 0, 1055, 1057, 3, 155, 70, 0, 1056, 1054, 1, 0, 0, 0, 1056, 1055, 1, 0, 0, 0, 1057, 1061, 1, 0, 0, 0, 1058, 1060, 3, 231, 108, 0, 1059, 1058, 1, 0, 0, 0, 1060, 1063, 1, 0, 0, 0, 1061, 1059, 1, 0, 0, 0, 1061, 1062, 1, 0, 0, 0, 1062, 1074, 1, 0, 0, 0, 1063, 1061, 1, 0, 0, 0, 1064, 1067, 3, 81, 33, 0, 1065, 1067, 3, 75, 30, 0, 1066, 1064, 1, 0, 0, 0, 1066, 1065, 1, 0, 0, 0, 1067, 1069, 1, 0, 0, 0, 1068, 1070, 3, 231, 108, 0, 1069, 1068, 1, 0, 0, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1069, 1, 0, 0, 0, 1071, 1072, 1, 0, 0, 0, 1072, 1074, 1, 0, 0, 0, 1073, 1056, 1, 0, 0, 0, 1073, 1066, 1, 0, 0, 0, 1074, 234, 1, 0, 0, 0, 1075, 1078, 3, 233, 109, 0, 1076, 1078, 3, 173, 79, 0, 1077, 1075, 1, 0, 0, 0, 1077, 1076, 1, 0, 0, 0, 1078, 1079, 1, 0, 0, 0, 1079, 1077, 1, 0, 0, 0, 1079, 1080, 1, 0, 0, 0, 1080, 236, 1, 0, 0, 0, 1081, 1082, 3, 55, 20, 0, 1082, 1083, 1, 0, 0, 0, 1083, 1084, 6, 111, 10, 0, 1084, 238, 1, 0, 0, 0, 1085, 1086, 3, 57, 21, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, 6, 112, 10, 0, 1088, 240, 1, 0, 0, 0, 1089, 1090, 3, 59, 22, 0, 1090, 1091, 1, 0, 0, 0, 1091, 1092, 6, 113, 10, 0, 1092, 242, 1, 0, 0, 0, 1093, 1094, 3, 63, 24, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 6, 114, 16, 0, 1096, 1097, 6, 114, 11, 0, 1097, 244, 1, 0, 0, 0, 1098, 1099, 3, 97, 41, 0, 1099, 1100, 1, 0, 0, 0, 1100, 1101, 6, 115, 19, 0, 1101, 246, 1, 0, 0, 0, 1102, 1103, 3, 101, 43, 0, 1103, 1104, 1, 0, 0, 0, 1104, 1105, 6, 116, 18, 0, 1105, 248, 1, 0, 0, 0, 1106, 1107, 3, 105, 45, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 117, 22, 0, 1109, 250, 1, 0, 0, 0, 1110, 1111, 4, 118, 6, 0, 1111, 1112, 3, 129, 57, 0, 1112, 1113, 1, 0, 0, 0, 1113, 1114, 6, 118, 23, 0, 1114, 252, 1, 0, 0, 0, 1115, 1116, 4, 119, 7, 0, 1116, 1117, 3, 165, 75, 0, 1117, 1118, 1, 0, 0, 0, 1118, 1119, 6, 119, 24, 0, 1119, 254, 1, 0, 0, 0, 1120, 1121, 7, 12, 0, 0, 1121, 1122, 7, 2, 0, 0, 1122, 256, 1, 0, 0, 0, 1123, 1124, 3, 235, 110, 0, 1124, 1125, 1, 0, 0, 0, 1125, 1126, 6, 121, 25, 0, 1126, 258, 1, 0, 0, 0, 1127, 1128, 3, 55, 20, 0, 1128, 1129, 1, 0, 0, 0, 1129, 1130, 6, 122, 10, 0, 1130, 260, 1, 0, 0, 0, 1131, 1132, 3, 57, 21, 0, 1132, 1133, 1, 0, 0, 0, 1133, 1134, 6, 123, 10, 0, 1134, 262, 1, 0, 0, 0, 1135, 1136, 3, 59, 22, 0, 1136, 1137, 1, 0, 0, 0, 1137, 1138, 6, 124, 10, 0, 1138, 264, 1, 0, 0, 0, 1139, 1140, 3, 63, 24, 0, 1140, 1141, 1, 0, 0, 0, 1141, 1142, 6, 125, 16, 0, 1142, 1143, 6, 125, 11, 0, 1143, 266, 1, 0, 0, 0, 1144, 1145, 3, 167, 76, 0, 1145, 1146, 1, 0, 0, 0, 1146, 1147, 6, 126, 14, 0, 1147, 1148, 6, 126, 26, 0, 1148, 268, 1, 0, 0, 0, 1149, 1150, 7, 7, 0, 0, 1150, 1151, 7, 9, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 1153, 6, 127, 27, 0, 1153, 270, 1, 0, 0, 0, 1154, 1155, 7, 19, 0, 0, 1155, 1156, 7, 1, 0, 0, 1156, 1157, 7, 5, 0, 0, 1157, 1158, 7, 10, 0, 0, 1158, 1159, 1, 0, 0, 0, 1159, 1160, 6, 128, 27, 0, 1160, 272, 1, 0, 0, 0, 1161, 1162, 8, 34, 0, 0, 1162, 274, 1, 0, 0, 0, 1163, 1165, 3, 273, 129, 0, 1164, 1163, 1, 0, 0, 0, 1165, 1166, 1, 0, 0, 0, 1166, 1164, 1, 0, 0, 0, 1166, 1167, 1, 0, 0, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1169, 3, 61, 23, 0, 1169, 1171, 1, 0, 0, 0, 1170, 1164, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 1173, 1, 0, 0, 0, 1172, 1174, 3, 273, 129, 0, 1173, 1172, 1, 0, 0, 0, 1174, 1175, 1, 0, 0, 0, 1175, 1173, 1, 0, 0, 0, 1175, 1176, 1, 0, 0, 0, 1176, 276, 1, 0, 0, 0, 1177, 1178, 3, 275, 130, 0, 1178, 1179, 1, 0, 0, 0, 1179, 1180, 6, 131, 28, 0, 1180, 278, 1, 0, 0, 0, 1181, 1182, 3, 55, 20, 0, 1182, 1183, 1, 0, 0, 0, 1183, 1184, 6, 132, 10, 0, 1184, 280, 1, 0, 0, 0, 1185, 1186, 3, 57, 21, 0, 1186, 1187, 1, 0, 0, 0, 1187, 1188, 6, 133, 10, 0, 1188, 282, 1, 0, 0, 0, 1189, 1190, 3, 59, 22, 0, 1190, 1191, 1, 0, 0, 0, 1191, 1192, 6, 134, 10, 0, 1192, 284, 1, 0, 0, 0, 1193, 1194, 3, 63, 24, 0, 1194, 1195, 1, 0, 0, 0, 1195, 1196, 6, 135, 16, 0, 1196, 1197, 6, 135, 11, 0, 1197, 1198, 6, 135, 11, 0, 1198, 286, 1, 0, 0, 0, 1199, 1200, 3, 97, 41, 0, 1200, 1201, 1, 0, 0, 0, 1201, 1202, 6, 136, 19, 0, 1202, 288, 1, 0, 0, 0, 1203, 1204, 3, 101, 43, 0, 1204, 1205, 1, 0, 0, 0, 1205, 1206, 6, 137, 18, 0, 1206, 290, 1, 0, 0, 0, 1207, 1208, 3, 105, 45, 0, 1208, 1209, 1, 0, 0, 0, 1209, 1210, 6, 138, 22, 0, 1210, 292, 1, 0, 0, 0, 1211, 1212, 3, 271, 128, 0, 1212, 1213, 1, 0, 0, 0, 1213, 1214, 6, 139, 29, 0, 1214, 294, 1, 0, 0, 0, 1215, 1216, 3, 235, 110, 0, 1216, 1217, 1, 0, 0, 0, 1217, 1218, 6, 140, 25, 0, 1218, 296, 1, 0, 0, 0, 1219, 1220, 3, 175, 80, 0, 1220, 1221, 1, 0, 0, 0, 1221, 1222, 6, 141, 30, 0, 1222, 298, 1, 0, 0, 0, 1223, 1224, 4, 142, 8, 0, 1224, 1225, 3, 129, 57, 0, 1225, 1226, 1, 0, 0, 0, 1226, 1227, 6, 142, 23, 0, 1227, 300, 1, 0, 0, 0, 1228, 1229, 4, 143, 9, 0, 1229, 1230, 3, 165, 75, 0, 1230, 1231, 1, 0, 0, 0, 1231, 1232, 6, 143, 24, 0, 1232, 302, 1, 0, 0, 0, 1233, 1234, 3, 55, 20, 0, 1234, 1235, 1, 0, 0, 0, 1235, 1236, 6, 144, 10, 0, 1236, 304, 1, 0, 0, 0, 1237, 1238, 3, 57, 21, 0, 1238, 1239, 1, 0, 0, 0, 1239, 1240, 6, 145, 10, 0, 1240, 306, 1, 0, 0, 0, 1241, 1242, 3, 59, 22, 0, 1242, 1243, 1, 0, 0, 0, 1243, 1244, 6, 146, 10, 0, 1244, 308, 1, 0, 0, 0, 1245, 1246, 3, 63, 24, 0, 1246, 1247, 1, 0, 0, 0, 1247, 1248, 6, 147, 16, 0, 1248, 1249, 6, 147, 11, 0, 1249, 310, 1, 0, 0, 0, 1250, 1251, 3, 105, 45, 0, 1251, 1252, 1, 0, 0, 0, 1252, 1253, 6, 148, 22, 0, 1253, 312, 1, 0, 0, 0, 1254, 1255, 4, 149, 10, 0, 1255, 1256, 3, 129, 57, 0, 1256, 1257, 1, 0, 0, 0, 1257, 1258, 6, 149, 23, 0, 1258, 314, 1, 0, 0, 0, 1259, 1260, 4, 150, 11, 0, 1260, 1261, 3, 165, 75, 0, 1261, 1262, 1, 0, 0, 0, 1262, 1263, 6, 150, 24, 0, 1263, 316, 1, 0, 0, 0, 1264, 1265, 3, 175, 80, 0, 1265, 1266, 1, 0, 0, 0, 1266, 1267, 6, 151, 30, 0, 1267, 318, 1, 0, 0, 0, 1268, 1269, 3, 171, 78, 0, 1269, 1270, 1, 0, 0, 0, 1270, 1271, 6, 152, 31, 0, 1271, 320, 1, 0, 0, 0, 1272, 1273, 3, 55, 20, 0, 1273, 1274, 1, 0, 0, 0, 1274, 1275, 6, 153, 10, 0, 1275, 322, 1, 0, 0, 0, 1276, 1277, 3, 57, 21, 0, 1277, 1278, 1, 0, 0, 0, 1278, 1279, 6, 154, 10, 0, 1279, 324, 1, 0, 0, 0, 1280, 1281, 3, 59, 22, 0, 1281, 1282, 1, 0, 0, 0, 1282, 1283, 6, 155, 10, 0, 1283, 326, 1, 0, 0, 0, 1284, 1285, 3, 63, 24, 0, 1285, 1286, 1, 0, 0, 0, 1286, 1287, 6, 156, 16, 0, 1287, 1288, 6, 156, 11, 0, 1288, 328, 1, 0, 0, 0, 1289, 1290, 7, 1, 0, 0, 1290, 1291, 7, 9, 0, 0, 1291, 1292, 7, 15, 0, 0, 1292, 1293, 7, 7, 0, 0, 1293, 330, 1, 0, 0, 0, 1294, 1295, 3, 55, 20, 0, 1295, 1296, 1, 0, 0, 0, 1296, 1297, 6, 158, 10, 0, 1297, 332, 1, 0, 0, 0, 1298, 1299, 3, 57, 21, 0, 1299, 1300, 1, 0, 0, 0, 1300, 1301, 6, 159, 10, 0, 1301, 334, 1, 0, 0, 0, 1302, 1303, 3, 59, 22, 0, 1303, 1304, 1, 0, 0, 0, 1304, 1305, 6, 160, 10, 0, 1305, 336, 1, 0, 0, 0, 1306, 1307, 3, 169, 77, 0, 1307, 1308, 1, 0, 0, 0, 1308, 1309, 6, 161, 17, 0, 1309, 1310, 6, 161, 11, 0, 1310, 338, 1, 0, 0, 0, 1311, 1312, 3, 61, 23, 0, 1312, 1313, 1, 0, 0, 0, 1313, 1314, 6, 162, 12, 0, 1314, 340, 1, 0, 0, 0, 1315, 1321, 3, 75, 30, 0, 1316, 1321, 3, 65, 25, 0, 1317, 1321, 3, 105, 45, 0, 1318, 1321, 3, 67, 26, 0, 1319, 1321, 3, 81, 33, 0, 1320, 1315, 1, 0, 0, 0, 1320, 1316, 1, 0, 0, 0, 1320, 1317, 1, 0, 0, 0, 1320, 1318, 1, 0, 0, 0, 1320, 1319, 1, 0, 0, 0, 1321, 1322, 1, 0, 0, 0, 1322, 1320, 1, 0, 0, 0, 1322, 1323, 1, 0, 0, 0, 1323, 342, 1, 0, 0, 0, 1324, 1325, 3, 55, 20, 0, 1325, 1326, 1, 0, 0, 0, 1326, 1327, 6, 164, 10, 0, 1327, 344, 1, 0, 0, 0, 1328, 1329, 3, 57, 21, 0, 1329, 1330, 1, 0, 0, 0, 1330, 1331, 6, 165, 10, 0, 1331, 346, 1, 0, 0, 0, 1332, 1333, 3, 59, 22, 0, 1333, 1334, 1, 0, 0, 0, 1334, 1335, 6, 166, 10, 0, 1335, 348, 1, 0, 0, 0, 1336, 1337, 3, 63, 24, 0, 1337, 1338, 1, 0, 0, 0, 1338, 1339, 6, 167, 16, 0, 1339, 1340, 6, 167, 11, 0, 1340, 350, 1, 0, 0, 0, 1341, 1342, 3, 61, 23, 0, 1342, 1343, 1, 0, 0, 0, 1343, 1344, 6, 168, 12, 0, 1344, 352, 1, 0, 0, 0, 1345, 1346, 3, 101, 43, 0, 1346, 1347, 1, 0, 0, 0, 1347, 1348, 6, 169, 18, 0, 1348, 354, 1, 0, 0, 0, 1349, 1350, 3, 105, 45, 0, 1350, 1351, 1, 0, 0, 0, 1351, 1352, 6, 170, 22, 0, 1352, 356, 1, 0, 0, 0, 1353, 1354, 3, 269, 127, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 171, 32, 0, 1356, 1357, 6, 171, 33, 0, 1357, 358, 1, 0, 0, 0, 1358, 1359, 3, 209, 97, 0, 1359, 1360, 1, 0, 0, 0, 1360, 1361, 6, 172, 20, 0, 1361, 360, 1, 0, 0, 0, 1362, 1363, 3, 85, 35, 0, 1363, 1364, 1, 0, 0, 0, 1364, 1365, 6, 173, 21, 0, 1365, 362, 1, 0, 0, 0, 1366, 1367, 3, 55, 20, 0, 1367, 1368, 1, 0, 0, 0, 1368, 1369, 6, 174, 10, 0, 1369, 364, 1, 0, 0, 0, 1370, 1371, 3, 57, 21, 0, 1371, 1372, 1, 0, 0, 0, 1372, 1373, 6, 175, 10, 0, 1373, 366, 1, 0, 0, 0, 1374, 1375, 3, 59, 22, 0, 1375, 1376, 1, 0, 0, 0, 1376, 1377, 6, 176, 10, 0, 1377, 368, 1, 0, 0, 0, 1378, 1379, 3, 63, 24, 0, 1379, 1380, 1, 0, 0, 0, 1380, 1381, 6, 177, 16, 0, 1381, 1382, 6, 177, 11, 0, 1382, 1383, 6, 177, 11, 0, 1383, 370, 1, 0, 0, 0, 1384, 1385, 3, 101, 43, 0, 1385, 1386, 1, 0, 0, 0, 1386, 1387, 6, 178, 18, 0, 1387, 372, 1, 0, 0, 0, 1388, 1389, 3, 105, 45, 0, 1389, 1390, 1, 0, 0, 0, 1390, 1391, 6, 179, 22, 0, 1391, 374, 1, 0, 0, 0, 1392, 1393, 3, 235, 110, 0, 1393, 1394, 1, 0, 0, 0, 1394, 1395, 6, 180, 25, 0, 1395, 376, 1, 0, 0, 0, 1396, 1397, 3, 55, 20, 0, 1397, 1398, 1, 0, 0, 0, 1398, 1399, 6, 181, 10, 0, 1399, 378, 1, 0, 0, 0, 1400, 1401, 3, 57, 21, 0, 1401, 1402, 1, 0, 0, 0, 1402, 1403, 6, 182, 10, 0, 1403, 380, 1, 0, 0, 0, 1404, 1405, 3, 59, 22, 0, 1405, 1406, 1, 0, 0, 0, 1406, 1407, 6, 183, 10, 0, 1407, 382, 1, 0, 0, 0, 1408, 1409, 3, 63, 24, 0, 1409, 1410, 1, 0, 0, 0, 1410, 1411, 6, 184, 16, 0, 1411, 1412, 6, 184, 11, 0, 1412, 384, 1, 0, 0, 0, 1413, 1414, 3, 209, 97, 0, 1414, 1415, 1, 0, 0, 0, 1415, 1416, 6, 185, 20, 0, 1416, 1417, 6, 185, 11, 0, 1417, 1418, 6, 185, 34, 0, 1418, 386, 1, 0, 0, 0, 1419, 1420, 3, 85, 35, 0, 1420, 1421, 1, 0, 0, 0, 1421, 1422, 6, 186, 21, 0, 1422, 1423, 6, 186, 11, 0, 1423, 1424, 6, 186, 34, 0, 1424, 388, 1, 0, 0, 0, 1425, 1426, 3, 55, 20, 0, 1426, 1427, 1, 0, 0, 0, 1427, 1428, 6, 187, 10, 0, 1428, 390, 1, 0, 0, 0, 1429, 1430, 3, 57, 21, 0, 1430, 1431, 1, 0, 0, 0, 1431, 1432, 6, 188, 10, 0, 1432, 392, 1, 0, 0, 0, 1433, 1434, 3, 59, 22, 0, 1434, 1435, 1, 0, 0, 0, 1435, 1436, 6, 189, 10, 0, 1436, 394, 1, 0, 0, 0, 1437, 1438, 3, 61, 23, 0, 1438, 1439, 1, 0, 0, 0, 1439, 1440, 6, 190, 12, 0, 1440, 1441, 6, 190, 11, 0, 1441, 1442, 6, 190, 9, 0, 1442, 396, 1, 0, 0, 0, 1443, 1444, 3, 101, 43, 0, 1444, 1445, 1, 0, 0, 0, 1445, 1446, 6, 191, 18, 0, 1446, 1447, 6, 191, 11, 0, 1447, 1448, 6, 191, 9, 0, 1448, 398, 1, 0, 0, 0, 1449, 1450, 3, 55, 20, 0, 1450, 1451, 1, 0, 0, 0, 1451, 1452, 6, 192, 10, 0, 1452, 400, 1, 0, 0, 0, 1453, 1454, 3, 57, 21, 0, 1454, 1455, 1, 0, 0, 0, 1455, 1456, 6, 193, 10, 0, 1456, 402, 1, 0, 0, 0, 1457, 1458, 3, 59, 22, 0, 1458, 1459, 1, 0, 0, 0, 1459, 1460, 6, 194, 10, 0, 1460, 404, 1, 0, 0, 0, 1461, 1462, 3, 175, 80, 0, 1462, 1463, 1, 0, 0, 0, 1463, 1464, 6, 195, 11, 0, 1464, 1465, 6, 195, 0, 0, 1465, 1466, 6, 195, 30, 0, 1466, 406, 1, 0, 0, 0, 1467, 1468, 3, 171, 78, 0, 1468, 1469, 1, 0, 0, 0, 1469, 1470, 6, 196, 11, 0, 1470, 1471, 6, 196, 0, 0, 1471, 1472, 6, 196, 31, 0, 1472, 408, 1, 0, 0, 0, 1473, 1474, 3, 91, 38, 0, 1474, 1475, 1, 0, 0, 0, 1475, 1476, 6, 197, 11, 0, 1476, 1477, 6, 197, 0, 0, 1477, 1478, 6, 197, 35, 0, 1478, 410, 1, 0, 0, 0, 1479, 1480, 3, 63, 24, 0, 1480, 1481, 1, 0, 0, 0, 1481, 1482, 6, 198, 16, 0, 1482, 1483, 6, 198, 11, 0, 1483, 412, 1, 0, 0, 0, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 581, 591, 595, 598, 607, 609, 620, 641, 646, 655, 662, 667, 669, 680, 688, 691, 693, 698, 703, 709, 716, 721, 727, 730, 738, 742, 873, 878, 885, 887, 903, 908, 913, 915, 921, 998, 1003, 1052, 1056, 1061, 1066, 1071, 1073, 1077, 1079, 1166, 1170, 1175, 1320, 1322, 36, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 24, 0, 7, 16, 0, 7, 65, 0, 5, 0, 0, 7, 25, 0, 7, 66, 0, 7, 34, 0, 7, 32, 0, 7, 76, 0, 7, 26, 0, 7, 36, 0, 7, 48, 0, 7, 64, 0, 7, 80, 0, 5, 10, 0, 5, 7, 0, 7, 90, 0, 7, 89, 0, 7, 68, 0, 7, 67, 0, 7, 88, 0, 5, 12, 0, 5, 14, 0, 7, 29, 0] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens index 4d1f42628914..3dd1a2c75403 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens @@ -21,46 +21,46 @@ UNKNOWN_CMD=20 LINE_COMMENT=21 MULTILINE_COMMENT=22 WS=23 -PIPE=24 -QUOTED_STRING=25 -INTEGER_LITERAL=26 -DECIMAL_LITERAL=27 -BY=28 -AND=29 -ASC=30 -ASSIGN=31 -CAST_OP=32 -COMMA=33 -DESC=34 -DOT=35 -FALSE=36 -FIRST=37 -IN=38 -IS=39 -LAST=40 -LIKE=41 -LP=42 -NOT=43 -NULL=44 -NULLS=45 -OR=46 -PARAM=47 -RLIKE=48 -RP=49 -TRUE=50 -EQ=51 -CIEQ=52 -NEQ=53 -LT=54 -LTE=55 -GT=56 -GTE=57 -PLUS=58 -MINUS=59 -ASTERISK=60 -SLASH=61 -PERCENT=62 -MATCH=63 +COLON=24 +PIPE=25 +QUOTED_STRING=26 +INTEGER_LITERAL=27 +DECIMAL_LITERAL=28 +BY=29 +AND=30 +ASC=31 +ASSIGN=32 +CAST_OP=33 +COMMA=34 +DESC=35 +DOT=36 +FALSE=37 +FIRST=38 +IN=39 +IS=40 +LAST=41 +LIKE=42 +LP=43 +NOT=44 +NULL=45 +NULLS=46 +OR=47 +PARAM=48 +RLIKE=49 +RP=50 +TRUE=51 +EQ=52 +CIEQ=53 +NEQ=54 +LT=55 +LTE=56 +GT=57 +GTE=58 +PLUS=59 +MINUS=60 +ASTERISK=61 +SLASH=62 +PERCENT=63 NAMED_OR_POSITIONAL_PARAM=64 OPENING_BRACKET=65 CLOSING_BRACKET=66 @@ -101,23 +101,22 @@ INFO=100 SHOW_LINE_COMMENT=101 SHOW_MULTILINE_COMMENT=102 SHOW_WS=103 -COLON=104 -SETTING=105 -SETTING_LINE_COMMENT=106 -SETTTING_MULTILINE_COMMENT=107 -SETTING_WS=108 -LOOKUP_LINE_COMMENT=109 -LOOKUP_MULTILINE_COMMENT=110 -LOOKUP_WS=111 -LOOKUP_FIELD_LINE_COMMENT=112 -LOOKUP_FIELD_MULTILINE_COMMENT=113 -LOOKUP_FIELD_WS=114 -METRICS_LINE_COMMENT=115 -METRICS_MULTILINE_COMMENT=116 -METRICS_WS=117 -CLOSING_METRICS_LINE_COMMENT=118 -CLOSING_METRICS_MULTILINE_COMMENT=119 -CLOSING_METRICS_WS=120 +SETTING=104 +SETTING_LINE_COMMENT=105 +SETTTING_MULTILINE_COMMENT=106 +SETTING_WS=107 +LOOKUP_LINE_COMMENT=108 +LOOKUP_MULTILINE_COMMENT=109 +LOOKUP_WS=110 +LOOKUP_FIELD_LINE_COMMENT=111 +LOOKUP_FIELD_MULTILINE_COMMENT=112 +LOOKUP_FIELD_WS=113 +METRICS_LINE_COMMENT=114 +METRICS_MULTILINE_COMMENT=115 +METRICS_WS=116 +CLOSING_METRICS_LINE_COMMENT=117 +CLOSING_METRICS_MULTILINE_COMMENT=118 +CLOSING_METRICS_WS=119 'dissect'=1 'drop'=2 'enrich'=3 @@ -134,47 +133,46 @@ CLOSING_METRICS_WS=120 'sort'=14 'stats'=15 'where'=16 -'|'=24 -'by'=28 -'and'=29 -'asc'=30 -'='=31 -'::'=32 -','=33 -'desc'=34 -'.'=35 -'false'=36 -'first'=37 -'in'=38 -'is'=39 -'last'=40 -'like'=41 -'('=42 -'not'=43 -'null'=44 -'nulls'=45 -'or'=46 -'?'=47 -'rlike'=48 -')'=49 -'true'=50 -'=='=51 -'=~'=52 -'!='=53 -'<'=54 -'<='=55 -'>'=56 -'>='=57 -'+'=58 -'-'=59 -'*'=60 -'/'=61 -'%'=62 -'match'=63 +':'=24 +'|'=25 +'by'=29 +'and'=30 +'asc'=31 +'='=32 +'::'=33 +','=34 +'desc'=35 +'.'=36 +'false'=37 +'first'=38 +'in'=39 +'is'=40 +'last'=41 +'like'=42 +'('=43 +'not'=44 +'null'=45 +'nulls'=46 +'or'=47 +'?'=48 +'rlike'=49 +')'=50 +'true'=51 +'=='=52 +'=~'=53 +'!='=54 +'<'=55 +'<='=56 +'>'=57 +'>='=58 +'+'=59 +'-'=60 +'*'=61 +'/'=62 +'%'=63 ']'=66 'metadata'=75 'as'=84 'on'=88 'with'=89 'info'=100 -':'=104 diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts index 589148bf08c7..54546fef8590 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts @@ -46,46 +46,46 @@ export default class esql_lexer extends lexer_config { public static readonly LINE_COMMENT = 21; public static readonly MULTILINE_COMMENT = 22; public static readonly WS = 23; - public static readonly PIPE = 24; - public static readonly QUOTED_STRING = 25; - public static readonly INTEGER_LITERAL = 26; - public static readonly DECIMAL_LITERAL = 27; - public static readonly BY = 28; - public static readonly AND = 29; - public static readonly ASC = 30; - public static readonly ASSIGN = 31; - public static readonly CAST_OP = 32; - public static readonly COMMA = 33; - public static readonly DESC = 34; - public static readonly DOT = 35; - public static readonly FALSE = 36; - public static readonly FIRST = 37; - public static readonly IN = 38; - public static readonly IS = 39; - public static readonly LAST = 40; - public static readonly LIKE = 41; - public static readonly LP = 42; - public static readonly NOT = 43; - public static readonly NULL = 44; - public static readonly NULLS = 45; - public static readonly OR = 46; - public static readonly PARAM = 47; - public static readonly RLIKE = 48; - public static readonly RP = 49; - public static readonly TRUE = 50; - public static readonly EQ = 51; - public static readonly CIEQ = 52; - public static readonly NEQ = 53; - public static readonly LT = 54; - public static readonly LTE = 55; - public static readonly GT = 56; - public static readonly GTE = 57; - public static readonly PLUS = 58; - public static readonly MINUS = 59; - public static readonly ASTERISK = 60; - public static readonly SLASH = 61; - public static readonly PERCENT = 62; - public static readonly MATCH = 63; + public static readonly COLON = 24; + public static readonly PIPE = 25; + public static readonly QUOTED_STRING = 26; + public static readonly INTEGER_LITERAL = 27; + public static readonly DECIMAL_LITERAL = 28; + public static readonly BY = 29; + public static readonly AND = 30; + public static readonly ASC = 31; + public static readonly ASSIGN = 32; + public static readonly CAST_OP = 33; + public static readonly COMMA = 34; + public static readonly DESC = 35; + public static readonly DOT = 36; + public static readonly FALSE = 37; + public static readonly FIRST = 38; + public static readonly IN = 39; + public static readonly IS = 40; + public static readonly LAST = 41; + public static readonly LIKE = 42; + public static readonly LP = 43; + public static readonly NOT = 44; + public static readonly NULL = 45; + public static readonly NULLS = 46; + public static readonly OR = 47; + public static readonly PARAM = 48; + public static readonly RLIKE = 49; + public static readonly RP = 50; + public static readonly TRUE = 51; + public static readonly EQ = 52; + public static readonly CIEQ = 53; + public static readonly NEQ = 54; + public static readonly LT = 55; + public static readonly LTE = 56; + public static readonly GT = 57; + public static readonly GTE = 58; + public static readonly PLUS = 59; + public static readonly MINUS = 60; + public static readonly ASTERISK = 61; + public static readonly SLASH = 62; + public static readonly PERCENT = 63; public static readonly NAMED_OR_POSITIONAL_PARAM = 64; public static readonly OPENING_BRACKET = 65; public static readonly CLOSING_BRACKET = 66; @@ -126,23 +126,22 @@ export default class esql_lexer extends lexer_config { public static readonly SHOW_LINE_COMMENT = 101; public static readonly SHOW_MULTILINE_COMMENT = 102; public static readonly SHOW_WS = 103; - public static readonly COLON = 104; - public static readonly SETTING = 105; - public static readonly SETTING_LINE_COMMENT = 106; - public static readonly SETTTING_MULTILINE_COMMENT = 107; - public static readonly SETTING_WS = 108; - public static readonly LOOKUP_LINE_COMMENT = 109; - public static readonly LOOKUP_MULTILINE_COMMENT = 110; - public static readonly LOOKUP_WS = 111; - public static readonly LOOKUP_FIELD_LINE_COMMENT = 112; - public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 113; - public static readonly LOOKUP_FIELD_WS = 114; - public static readonly METRICS_LINE_COMMENT = 115; - public static readonly METRICS_MULTILINE_COMMENT = 116; - public static readonly METRICS_WS = 117; - public static readonly CLOSING_METRICS_LINE_COMMENT = 118; - public static readonly CLOSING_METRICS_MULTILINE_COMMENT = 119; - public static readonly CLOSING_METRICS_WS = 120; + public static readonly SETTING = 104; + public static readonly SETTING_LINE_COMMENT = 105; + public static readonly SETTTING_MULTILINE_COMMENT = 106; + public static readonly SETTING_WS = 107; + public static readonly LOOKUP_LINE_COMMENT = 108; + public static readonly LOOKUP_MULTILINE_COMMENT = 109; + public static readonly LOOKUP_WS = 110; + public static readonly LOOKUP_FIELD_LINE_COMMENT = 111; + public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 112; + public static readonly LOOKUP_FIELD_WS = 113; + public static readonly METRICS_LINE_COMMENT = 114; + public static readonly METRICS_MULTILINE_COMMENT = 115; + public static readonly METRICS_WS = 116; + public static readonly CLOSING_METRICS_LINE_COMMENT = 117; + public static readonly CLOSING_METRICS_MULTILINE_COMMENT = 118; + public static readonly CLOSING_METRICS_WS = 119; public static readonly EOF = Token.EOF; public static readonly EXPRESSION_MODE = 1; public static readonly EXPLAIN_MODE = 2; @@ -173,26 +172,26 @@ export default class esql_lexer extends lexer_config { null, null, null, null, null, null, - "'|'", null, + "':'", "'|'", null, null, - "'by'", "'and'", - "'asc'", "'='", - "'::'", "','", - "'desc'", "'.'", - "'false'", "'first'", - "'in'", "'is'", - "'last'", "'like'", - "'('", "'not'", - "'null'", "'nulls'", - "'or'", "'?'", - "'rlike'", "')'", - "'true'", "'=='", - "'=~'", "'!='", - "'<'", "'<='", - "'>'", "'>='", - "'+'", "'-'", - "'*'", "'/'", - "'%'", "'match'", + null, "'by'", + "'and'", "'asc'", + "'='", "'::'", + "','", "'desc'", + "'.'", "'false'", + "'first'", "'in'", + "'is'", "'last'", + "'like'", "'('", + "'not'", "'null'", + "'nulls'", "'or'", + "'?'", "'rlike'", + "')'", "'true'", + "'=='", "'=~'", + "'!='", "'<'", + "'<='", "'>'", + "'>='", "'+'", + "'-'", "'*'", + "'/'", "'%'", null, null, "']'", null, null, null, @@ -211,9 +210,7 @@ export default class esql_lexer extends lexer_config { null, null, null, null, null, null, - "'info'", null, - null, null, - "':'" ]; + "'info'" ]; public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", @@ -229,8 +226,8 @@ export default class esql_lexer extends lexer_config { "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", - "WS", "PIPE", - "QUOTED_STRING", + "WS", "COLON", + "PIPE", "QUOTED_STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", @@ -251,7 +248,7 @@ export default class esql_lexer extends lexer_config { "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "MATCH", "NAMED_OR_POSITIONAL_PARAM", + "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", @@ -288,7 +285,7 @@ export default class esql_lexer extends lexer_config { "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", "SHOW_WS", - "COLON", "SETTING", + "SETTING", "SETTING_LINE_COMMENT", "SETTTING_MULTILINE_COMMENT", "SETTING_WS", @@ -317,13 +314,13 @@ export default class esql_lexer extends lexer_config { "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", "FROM", "GROK", "KEEP", "LIMIT", "MV_EXPAND", "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", "UNKNOWN_CMD", "LINE_COMMENT", - "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", + "MULTILINE_COMMENT", "WS", "COLON", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", "ASPERAND", "BACKQUOTE", "BACKQUOTE_BLOCK", "UNDERSCORE", "UNQUOTED_ID_BODY", "QUOTED_STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "CAST_OP", "COMMA", "DESC", "DOT", "FALSE", "FIRST", "IN", "IS", "LAST", "LIKE", "LP", "NOT", "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "EQ", "CIEQ", "NEQ", "LT", - "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "MATCH", + "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "EXPRESSION_COLON", "NESTED_WHERE", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "EXPLAIN_OPENING_BRACKET", "EXPLAIN_PIPE", @@ -346,7 +343,7 @@ export default class esql_lexer extends lexer_config { "MVEXPAND_DOT", "MVEXPAND_PARAM", "MVEXPAND_NAMED_OR_POSITIONAL_PARAM", "MVEXPAND_QUOTED_IDENTIFIER", "MVEXPAND_UNQUOTED_IDENTIFIER", "MVEXPAND_LINE_COMMENT", "MVEXPAND_MULTILINE_COMMENT", "MVEXPAND_WS", "SHOW_PIPE", "INFO", "SHOW_LINE_COMMENT", - "SHOW_MULTILINE_COMMENT", "SHOW_WS", "SETTING_CLOSING_BRACKET", "COLON", + "SHOW_MULTILINE_COMMENT", "SHOW_WS", "SETTING_CLOSING_BRACKET", "SETTING_COLON", "SETTING", "SETTING_LINE_COMMENT", "SETTTING_MULTILINE_COMMENT", "SETTING_WS", "LOOKUP_PIPE", "LOOKUP_COLON", "LOOKUP_COMMA", "LOOKUP_DOT", "LOOKUP_ON", "LOOKUP_UNQUOTED_SOURCE", "LOOKUP_QUOTED_SOURCE", "LOOKUP_LINE_COMMENT", @@ -386,21 +383,23 @@ export default class esql_lexer extends lexer_config { return this.DEV_LOOKUP_sempred(localctx, predIndex); case 18: return this.DEV_METRICS_sempred(localctx, predIndex); - case 105: - return this.PROJECT_PARAM_sempred(localctx, predIndex); + case 73: + return this.EXPRESSION_COLON_sempred(localctx, predIndex); case 106: + return this.PROJECT_PARAM_sempred(localctx, predIndex); + case 107: return this.PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 117: - return this.RENAME_PARAM_sempred(localctx, predIndex); case 118: + return this.RENAME_PARAM_sempred(localctx, predIndex); + case 119: return this.RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 141: - return this.ENRICH_FIELD_PARAM_sempred(localctx, predIndex); case 142: + return this.ENRICH_FIELD_PARAM_sempred(localctx, predIndex); + case 143: return this.ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 148: - return this.MVEXPAND_PARAM_sempred(localctx, predIndex); case 149: + return this.MVEXPAND_PARAM_sempred(localctx, predIndex); + case 150: return this.MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); } return true; @@ -426,64 +425,71 @@ export default class esql_lexer extends lexer_config { } return true; } - private PROJECT_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private EXPRESSION_COLON_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 3: return this.isDevVersion(); } return true; } - private PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private PROJECT_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 4: return this.isDevVersion(); } return true; } - private RENAME_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 5: return this.isDevVersion(); } return true; } - private RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private RENAME_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 6: return this.isDevVersion(); } return true; } - private ENRICH_FIELD_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 7: return this.isDevVersion(); } return true; } - private ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private ENRICH_FIELD_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 8: return this.isDevVersion(); } return true; } - private MVEXPAND_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 9: return this.isDevVersion(); } return true; } - private MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + private MVEXPAND_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { case 10: return this.isDevVersion(); } return true; } + private MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { + switch (predIndex) { + case 11: + return this.isDevVersion(); + } + return true; + } - public static readonly _serializedATN: number[] = [4,0,120,1479,6,-1,6, + public static readonly _serializedATN: number[] = [4,0,119,1484,6,-1,6, -1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,2,0, 7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9, 7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7, @@ -514,483 +520,485 @@ export default class esql_lexer extends lexer_config { 2,175,7,175,2,176,7,176,2,177,7,177,2,178,7,178,2,179,7,179,2,180,7,180, 2,181,7,181,2,182,7,182,2,183,7,183,2,184,7,184,2,185,7,185,2,186,7,186, 2,187,7,187,2,188,7,188,2,189,7,189,2,190,7,190,2,191,7,191,2,192,7,192, - 2,193,7,193,2,194,7,194,2,195,7,195,2,196,7,196,2,197,7,197,1,0,1,0,1,0, - 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,2, - 1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,4, - 1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6, - 1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9, - 1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10, - 1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1, - 12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14, - 1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1, - 16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17, - 1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, - 18,1,18,1,19,4,19,578,8,19,11,19,12,19,579,1,19,1,19,1,20,1,20,1,20,1,20, - 5,20,588,8,20,10,20,12,20,591,9,20,1,20,3,20,594,8,20,1,20,3,20,597,8,20, - 1,20,1,20,1,21,1,21,1,21,1,21,1,21,5,21,606,8,21,10,21,12,21,609,9,21,1, - 21,1,21,1,21,1,21,1,21,1,22,4,22,617,8,22,11,22,12,22,618,1,22,1,22,1,23, - 1,23,1,23,1,23,1,24,1,24,1,25,1,25,1,26,1,26,1,26,1,27,1,27,1,28,1,28,3, - 28,638,8,28,1,28,4,28,641,8,28,11,28,12,28,642,1,29,1,29,1,30,1,30,1,31, - 1,31,1,31,3,31,652,8,31,1,32,1,32,1,33,1,33,1,33,3,33,659,8,33,1,34,1,34, - 1,34,5,34,664,8,34,10,34,12,34,667,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5, - 34,675,8,34,10,34,12,34,678,9,34,1,34,1,34,1,34,1,34,1,34,3,34,685,8,34, - 1,34,3,34,688,8,34,3,34,690,8,34,1,35,4,35,693,8,35,11,35,12,35,694,1,36, - 4,36,698,8,36,11,36,12,36,699,1,36,1,36,5,36,704,8,36,10,36,12,36,707,9, - 36,1,36,1,36,4,36,711,8,36,11,36,12,36,712,1,36,4,36,716,8,36,11,36,12, - 36,717,1,36,1,36,5,36,722,8,36,10,36,12,36,725,9,36,3,36,727,8,36,1,36, - 1,36,1,36,1,36,4,36,733,8,36,11,36,12,36,734,1,36,1,36,3,36,739,8,36,1, - 37,1,37,1,37,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,40,1,40,1,41,1,41, - 1,41,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,45,1,45,1,45,1,45,1, - 45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,48,1,48,1,48,1,49, - 1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,52,1,52,1,52,1, - 52,1,53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55, - 1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,59,1,59,1,59,1,59,1, - 59,1,60,1,60,1,60,1,61,1,61,1,61,1,62,1,62,1,62,1,63,1,63,1,64,1,64,1,64, - 1,65,1,65,1,66,1,66,1,66,1,67,1,67,1,68,1,68,1,69,1,69,1,70,1,70,1,71,1, - 71,1,72,1,72,1,72,1,72,1,72,1,72,1,73,1,73,1,73,1,73,1,74,1,74,1,74,3,74, - 871,8,74,1,74,5,74,874,8,74,10,74,12,74,877,9,74,1,74,1,74,4,74,881,8,74, - 11,74,12,74,882,3,74,885,8,74,1,75,1,75,1,75,1,75,1,75,1,76,1,76,1,76,1, - 76,1,76,1,77,1,77,5,77,899,8,77,10,77,12,77,902,9,77,1,77,1,77,3,77,906, - 8,77,1,77,4,77,909,8,77,11,77,12,77,910,3,77,913,8,77,1,78,1,78,4,78,917, - 8,78,11,78,12,78,918,1,78,1,78,1,79,1,79,1,80,1,80,1,80,1,80,1,81,1,81, - 1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1, - 84,1,84,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88, - 1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,91,1,91,1, - 91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,94, - 1,94,1,94,1,94,1,94,1,95,1,95,1,95,3,95,996,8,95,1,96,4,96,999,8,96,11, - 96,12,96,1000,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1, - 99,1,100,1,100,1,100,1,100,1,101,1,101,1,101,1,101,1,102,1,102,1,102,1, - 102,1,102,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,1,105,1,105,1, - 105,1,105,1,105,1,106,1,106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,3, - 107,1050,8,107,1,108,1,108,3,108,1054,8,108,1,108,5,108,1057,8,108,10,108, - 12,108,1060,9,108,1,108,1,108,3,108,1064,8,108,1,108,4,108,1067,8,108,11, - 108,12,108,1068,3,108,1071,8,108,1,109,1,109,4,109,1075,8,109,11,109,12, - 109,1076,1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,112,1,112,1, - 112,1,112,1,113,1,113,1,113,1,113,1,113,1,114,1,114,1,114,1,114,1,115,1, - 115,1,115,1,115,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1,117,1, - 118,1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,120,1,120,1,120,1,120,1, - 121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123,1,123,1, - 124,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1, - 126,1,126,1,126,1,127,1,127,1,127,1,127,1,127,1,127,1,127,1,128,1,128,1, - 129,4,129,1162,8,129,11,129,12,129,1163,1,129,1,129,3,129,1168,8,129,1, - 129,4,129,1171,8,129,11,129,12,129,1172,1,130,1,130,1,130,1,130,1,131,1, - 131,1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,134,1, - 134,1,134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,136,1,136,1,136,1, - 136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1, - 139,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,141,1,142,1,142,1, - 142,1,142,1,142,1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145,1, - 145,1,145,1,145,1,146,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1, - 148,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149,1,149,1,150,1,150,1, - 150,1,150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,153,1,153,1, - 153,1,153,1,154,1,154,1,154,1,154,1,155,1,155,1,155,1,155,1,155,1,156,1, - 156,1,156,1,156,1,156,1,157,1,157,1,157,1,157,1,158,1,158,1,158,1,158,1, - 159,1,159,1,159,1,159,1,160,1,160,1,160,1,160,1,160,1,161,1,161,1,162,1, - 162,1,162,1,162,1,162,4,162,1316,8,162,11,162,12,162,1317,1,163,1,163,1, - 163,1,163,1,164,1,164,1,164,1,164,1,165,1,165,1,165,1,165,1,166,1,166,1, - 166,1,166,1,166,1,167,1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,169,1, - 169,1,169,1,169,1,170,1,170,1,170,1,170,1,170,1,171,1,171,1,171,1,171,1, - 172,1,172,1,172,1,172,1,173,1,173,1,173,1,173,1,174,1,174,1,174,1,174,1, - 175,1,175,1,175,1,175,1,176,1,176,1,176,1,176,1,176,1,176,1,177,1,177,1, - 177,1,177,1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179,1,180,1,180,1, - 180,1,180,1,181,1,181,1,181,1,181,1,182,1,182,1,182,1,182,1,183,1,183,1, - 183,1,183,1,183,1,184,1,184,1,184,1,184,1,184,1,184,1,185,1,185,1,185,1, - 185,1,185,1,185,1,186,1,186,1,186,1,186,1,187,1,187,1,187,1,187,1,188,1, - 188,1,188,1,188,1,189,1,189,1,189,1,189,1,189,1,189,1,190,1,190,1,190,1, - 190,1,190,1,190,1,191,1,191,1,191,1,191,1,192,1,192,1,192,1,192,1,193,1, - 193,1,193,1,193,1,194,1,194,1,194,1,194,1,194,1,194,1,195,1,195,1,195,1, - 195,1,195,1,195,1,196,1,196,1,196,1,196,1,196,1,196,1,197,1,197,1,197,1, - 197,1,197,2,607,676,0,198,15,1,17,2,19,3,21,4,23,5,25,6,27,7,29,8,31,9, - 33,10,35,11,37,12,39,13,41,14,43,15,45,16,47,17,49,18,51,19,53,20,55,21, - 57,22,59,23,61,24,63,0,65,0,67,0,69,0,71,0,73,0,75,0,77,0,79,0,81,0,83, - 25,85,26,87,27,89,28,91,29,93,30,95,31,97,32,99,33,101,34,103,35,105,36, - 107,37,109,38,111,39,113,40,115,41,117,42,119,43,121,44,123,45,125,46,127, - 47,129,48,131,49,133,50,135,51,137,52,139,53,141,54,143,55,145,56,147,57, - 149,58,151,59,153,60,155,61,157,62,159,63,161,0,163,64,165,65,167,66,169, - 67,171,0,173,68,175,69,177,70,179,71,181,0,183,0,185,72,187,73,189,74,191, - 0,193,0,195,0,197,0,199,0,201,0,203,75,205,0,207,76,209,0,211,0,213,77, - 215,78,217,79,219,0,221,0,223,0,225,0,227,0,229,0,231,0,233,80,235,81,237, - 82,239,83,241,0,243,0,245,0,247,0,249,0,251,0,253,84,255,0,257,85,259,86, - 261,87,263,0,265,0,267,88,269,89,271,0,273,90,275,0,277,91,279,92,281,93, - 283,0,285,0,287,0,289,0,291,0,293,0,295,0,297,0,299,0,301,94,303,95,305, - 96,307,0,309,0,311,0,313,0,315,0,317,0,319,97,321,98,323,99,325,0,327,100, - 329,101,331,102,333,103,335,0,337,104,339,105,341,106,343,107,345,108,347, - 0,349,0,351,0,353,0,355,0,357,0,359,0,361,109,363,110,365,111,367,0,369, - 0,371,0,373,0,375,112,377,113,379,114,381,0,383,0,385,0,387,115,389,116, - 391,117,393,0,395,0,397,118,399,119,401,120,403,0,405,0,407,0,409,0,15, - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,35,2,0,68,68,100,100,2,0,73,73,105,105, - 2,0,83,83,115,115,2,0,69,69,101,101,2,0,67,67,99,99,2,0,84,84,116,116,2, - 0,82,82,114,114,2,0,79,79,111,111,2,0,80,80,112,112,2,0,78,78,110,110,2, - 0,72,72,104,104,2,0,86,86,118,118,2,0,65,65,97,97,2,0,76,76,108,108,2,0, - 88,88,120,120,2,0,70,70,102,102,2,0,77,77,109,109,2,0,71,71,103,103,2,0, - 75,75,107,107,2,0,87,87,119,119,2,0,85,85,117,117,6,0,9,10,13,13,32,32, - 47,47,91,91,93,93,2,0,10,10,13,13,3,0,9,10,13,13,32,32,1,0,48,57,2,0,65, - 90,97,122,8,0,34,34,78,78,82,82,84,84,92,92,110,110,114,114,116,116,4,0, - 10,10,13,13,34,34,92,92,2,0,43,43,45,45,1,0,96,96,2,0,66,66,98,98,2,0,89, - 89,121,121,11,0,9,10,13,13,32,32,34,34,44,44,47,47,58,58,61,61,91,91,93, - 93,124,124,2,0,42,42,47,47,11,0,9,10,13,13,32,32,34,35,44,44,47,47,58,58, - 60,60,62,63,92,92,124,124,1507,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0, - 21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0, - 0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0, - 43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0, - 0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59,1,0,0,0,1,61,1,0,0,0,1,83,1,0,0,0,1, - 85,1,0,0,0,1,87,1,0,0,0,1,89,1,0,0,0,1,91,1,0,0,0,1,93,1,0,0,0,1,95,1,0, - 0,0,1,97,1,0,0,0,1,99,1,0,0,0,1,101,1,0,0,0,1,103,1,0,0,0,1,105,1,0,0,0, - 1,107,1,0,0,0,1,109,1,0,0,0,1,111,1,0,0,0,1,113,1,0,0,0,1,115,1,0,0,0,1, - 117,1,0,0,0,1,119,1,0,0,0,1,121,1,0,0,0,1,123,1,0,0,0,1,125,1,0,0,0,1,127, - 1,0,0,0,1,129,1,0,0,0,1,131,1,0,0,0,1,133,1,0,0,0,1,135,1,0,0,0,1,137,1, - 0,0,0,1,139,1,0,0,0,1,141,1,0,0,0,1,143,1,0,0,0,1,145,1,0,0,0,1,147,1,0, - 0,0,1,149,1,0,0,0,1,151,1,0,0,0,1,153,1,0,0,0,1,155,1,0,0,0,1,157,1,0,0, - 0,1,159,1,0,0,0,1,161,1,0,0,0,1,163,1,0,0,0,1,165,1,0,0,0,1,167,1,0,0,0, - 1,169,1,0,0,0,1,173,1,0,0,0,1,175,1,0,0,0,1,177,1,0,0,0,1,179,1,0,0,0,2, - 181,1,0,0,0,2,183,1,0,0,0,2,185,1,0,0,0,2,187,1,0,0,0,2,189,1,0,0,0,3,191, - 1,0,0,0,3,193,1,0,0,0,3,195,1,0,0,0,3,197,1,0,0,0,3,199,1,0,0,0,3,201,1, - 0,0,0,3,203,1,0,0,0,3,207,1,0,0,0,3,209,1,0,0,0,3,211,1,0,0,0,3,213,1,0, - 0,0,3,215,1,0,0,0,3,217,1,0,0,0,4,219,1,0,0,0,4,221,1,0,0,0,4,223,1,0,0, - 0,4,225,1,0,0,0,4,227,1,0,0,0,4,233,1,0,0,0,4,235,1,0,0,0,4,237,1,0,0,0, - 4,239,1,0,0,0,5,241,1,0,0,0,5,243,1,0,0,0,5,245,1,0,0,0,5,247,1,0,0,0,5, - 249,1,0,0,0,5,251,1,0,0,0,5,253,1,0,0,0,5,255,1,0,0,0,5,257,1,0,0,0,5,259, - 1,0,0,0,5,261,1,0,0,0,6,263,1,0,0,0,6,265,1,0,0,0,6,267,1,0,0,0,6,269,1, - 0,0,0,6,273,1,0,0,0,6,275,1,0,0,0,6,277,1,0,0,0,6,279,1,0,0,0,6,281,1,0, - 0,0,7,283,1,0,0,0,7,285,1,0,0,0,7,287,1,0,0,0,7,289,1,0,0,0,7,291,1,0,0, - 0,7,293,1,0,0,0,7,295,1,0,0,0,7,297,1,0,0,0,7,299,1,0,0,0,7,301,1,0,0,0, - 7,303,1,0,0,0,7,305,1,0,0,0,8,307,1,0,0,0,8,309,1,0,0,0,8,311,1,0,0,0,8, - 313,1,0,0,0,8,315,1,0,0,0,8,317,1,0,0,0,8,319,1,0,0,0,8,321,1,0,0,0,8,323, - 1,0,0,0,9,325,1,0,0,0,9,327,1,0,0,0,9,329,1,0,0,0,9,331,1,0,0,0,9,333,1, - 0,0,0,10,335,1,0,0,0,10,337,1,0,0,0,10,339,1,0,0,0,10,341,1,0,0,0,10,343, - 1,0,0,0,10,345,1,0,0,0,11,347,1,0,0,0,11,349,1,0,0,0,11,351,1,0,0,0,11, - 353,1,0,0,0,11,355,1,0,0,0,11,357,1,0,0,0,11,359,1,0,0,0,11,361,1,0,0,0, - 11,363,1,0,0,0,11,365,1,0,0,0,12,367,1,0,0,0,12,369,1,0,0,0,12,371,1,0, - 0,0,12,373,1,0,0,0,12,375,1,0,0,0,12,377,1,0,0,0,12,379,1,0,0,0,13,381, - 1,0,0,0,13,383,1,0,0,0,13,385,1,0,0,0,13,387,1,0,0,0,13,389,1,0,0,0,13, - 391,1,0,0,0,14,393,1,0,0,0,14,395,1,0,0,0,14,397,1,0,0,0,14,399,1,0,0,0, - 14,401,1,0,0,0,14,403,1,0,0,0,14,405,1,0,0,0,14,407,1,0,0,0,14,409,1,0, - 0,0,15,411,1,0,0,0,17,421,1,0,0,0,19,428,1,0,0,0,21,437,1,0,0,0,23,444, - 1,0,0,0,25,454,1,0,0,0,27,461,1,0,0,0,29,468,1,0,0,0,31,475,1,0,0,0,33, - 483,1,0,0,0,35,495,1,0,0,0,37,504,1,0,0,0,39,510,1,0,0,0,41,517,1,0,0,0, - 43,524,1,0,0,0,45,532,1,0,0,0,47,540,1,0,0,0,49,555,1,0,0,0,51,565,1,0, - 0,0,53,577,1,0,0,0,55,583,1,0,0,0,57,600,1,0,0,0,59,616,1,0,0,0,61,622, - 1,0,0,0,63,626,1,0,0,0,65,628,1,0,0,0,67,630,1,0,0,0,69,633,1,0,0,0,71, - 635,1,0,0,0,73,644,1,0,0,0,75,646,1,0,0,0,77,651,1,0,0,0,79,653,1,0,0,0, - 81,658,1,0,0,0,83,689,1,0,0,0,85,692,1,0,0,0,87,738,1,0,0,0,89,740,1,0, - 0,0,91,743,1,0,0,0,93,747,1,0,0,0,95,751,1,0,0,0,97,753,1,0,0,0,99,756, - 1,0,0,0,101,758,1,0,0,0,103,763,1,0,0,0,105,765,1,0,0,0,107,771,1,0,0,0, - 109,777,1,0,0,0,111,780,1,0,0,0,113,783,1,0,0,0,115,788,1,0,0,0,117,793, - 1,0,0,0,119,795,1,0,0,0,121,799,1,0,0,0,123,804,1,0,0,0,125,810,1,0,0,0, - 127,813,1,0,0,0,129,815,1,0,0,0,131,821,1,0,0,0,133,823,1,0,0,0,135,828, - 1,0,0,0,137,831,1,0,0,0,139,834,1,0,0,0,141,837,1,0,0,0,143,839,1,0,0,0, - 145,842,1,0,0,0,147,844,1,0,0,0,149,847,1,0,0,0,151,849,1,0,0,0,153,851, - 1,0,0,0,155,853,1,0,0,0,157,855,1,0,0,0,159,857,1,0,0,0,161,863,1,0,0,0, - 163,884,1,0,0,0,165,886,1,0,0,0,167,891,1,0,0,0,169,912,1,0,0,0,171,914, - 1,0,0,0,173,922,1,0,0,0,175,924,1,0,0,0,177,928,1,0,0,0,179,932,1,0,0,0, - 181,936,1,0,0,0,183,941,1,0,0,0,185,946,1,0,0,0,187,950,1,0,0,0,189,954, - 1,0,0,0,191,958,1,0,0,0,193,963,1,0,0,0,195,967,1,0,0,0,197,971,1,0,0,0, - 199,975,1,0,0,0,201,979,1,0,0,0,203,983,1,0,0,0,205,995,1,0,0,0,207,998, - 1,0,0,0,209,1002,1,0,0,0,211,1006,1,0,0,0,213,1010,1,0,0,0,215,1014,1,0, - 0,0,217,1018,1,0,0,0,219,1022,1,0,0,0,221,1027,1,0,0,0,223,1031,1,0,0,0, - 225,1035,1,0,0,0,227,1040,1,0,0,0,229,1049,1,0,0,0,231,1070,1,0,0,0,233, - 1074,1,0,0,0,235,1078,1,0,0,0,237,1082,1,0,0,0,239,1086,1,0,0,0,241,1090, - 1,0,0,0,243,1095,1,0,0,0,245,1099,1,0,0,0,247,1103,1,0,0,0,249,1107,1,0, - 0,0,251,1112,1,0,0,0,253,1117,1,0,0,0,255,1120,1,0,0,0,257,1124,1,0,0,0, - 259,1128,1,0,0,0,261,1132,1,0,0,0,263,1136,1,0,0,0,265,1141,1,0,0,0,267, - 1146,1,0,0,0,269,1151,1,0,0,0,271,1158,1,0,0,0,273,1167,1,0,0,0,275,1174, - 1,0,0,0,277,1178,1,0,0,0,279,1182,1,0,0,0,281,1186,1,0,0,0,283,1190,1,0, - 0,0,285,1196,1,0,0,0,287,1200,1,0,0,0,289,1204,1,0,0,0,291,1208,1,0,0,0, - 293,1212,1,0,0,0,295,1216,1,0,0,0,297,1220,1,0,0,0,299,1225,1,0,0,0,301, - 1230,1,0,0,0,303,1234,1,0,0,0,305,1238,1,0,0,0,307,1242,1,0,0,0,309,1247, - 1,0,0,0,311,1251,1,0,0,0,313,1256,1,0,0,0,315,1261,1,0,0,0,317,1265,1,0, - 0,0,319,1269,1,0,0,0,321,1273,1,0,0,0,323,1277,1,0,0,0,325,1281,1,0,0,0, - 327,1286,1,0,0,0,329,1291,1,0,0,0,331,1295,1,0,0,0,333,1299,1,0,0,0,335, - 1303,1,0,0,0,337,1308,1,0,0,0,339,1315,1,0,0,0,341,1319,1,0,0,0,343,1323, - 1,0,0,0,345,1327,1,0,0,0,347,1331,1,0,0,0,349,1336,1,0,0,0,351,1340,1,0, - 0,0,353,1344,1,0,0,0,355,1348,1,0,0,0,357,1353,1,0,0,0,359,1357,1,0,0,0, - 361,1361,1,0,0,0,363,1365,1,0,0,0,365,1369,1,0,0,0,367,1373,1,0,0,0,369, - 1379,1,0,0,0,371,1383,1,0,0,0,373,1387,1,0,0,0,375,1391,1,0,0,0,377,1395, - 1,0,0,0,379,1399,1,0,0,0,381,1403,1,0,0,0,383,1408,1,0,0,0,385,1414,1,0, - 0,0,387,1420,1,0,0,0,389,1424,1,0,0,0,391,1428,1,0,0,0,393,1432,1,0,0,0, - 395,1438,1,0,0,0,397,1444,1,0,0,0,399,1448,1,0,0,0,401,1452,1,0,0,0,403, - 1456,1,0,0,0,405,1462,1,0,0,0,407,1468,1,0,0,0,409,1474,1,0,0,0,411,412, - 7,0,0,0,412,413,7,1,0,0,413,414,7,2,0,0,414,415,7,2,0,0,415,416,7,3,0,0, - 416,417,7,4,0,0,417,418,7,5,0,0,418,419,1,0,0,0,419,420,6,0,0,0,420,16, - 1,0,0,0,421,422,7,0,0,0,422,423,7,6,0,0,423,424,7,7,0,0,424,425,7,8,0,0, - 425,426,1,0,0,0,426,427,6,1,1,0,427,18,1,0,0,0,428,429,7,3,0,0,429,430, - 7,9,0,0,430,431,7,6,0,0,431,432,7,1,0,0,432,433,7,4,0,0,433,434,7,10,0, - 0,434,435,1,0,0,0,435,436,6,2,2,0,436,20,1,0,0,0,437,438,7,3,0,0,438,439, - 7,11,0,0,439,440,7,12,0,0,440,441,7,13,0,0,441,442,1,0,0,0,442,443,6,3, - 0,0,443,22,1,0,0,0,444,445,7,3,0,0,445,446,7,14,0,0,446,447,7,8,0,0,447, - 448,7,13,0,0,448,449,7,12,0,0,449,450,7,1,0,0,450,451,7,9,0,0,451,452,1, - 0,0,0,452,453,6,4,3,0,453,24,1,0,0,0,454,455,7,15,0,0,455,456,7,6,0,0,456, - 457,7,7,0,0,457,458,7,16,0,0,458,459,1,0,0,0,459,460,6,5,4,0,460,26,1,0, - 0,0,461,462,7,17,0,0,462,463,7,6,0,0,463,464,7,7,0,0,464,465,7,18,0,0,465, - 466,1,0,0,0,466,467,6,6,0,0,467,28,1,0,0,0,468,469,7,18,0,0,469,470,7,3, - 0,0,470,471,7,3,0,0,471,472,7,8,0,0,472,473,1,0,0,0,473,474,6,7,1,0,474, - 30,1,0,0,0,475,476,7,13,0,0,476,477,7,1,0,0,477,478,7,16,0,0,478,479,7, - 1,0,0,479,480,7,5,0,0,480,481,1,0,0,0,481,482,6,8,0,0,482,32,1,0,0,0,483, - 484,7,16,0,0,484,485,7,11,0,0,485,486,5,95,0,0,486,487,7,3,0,0,487,488, - 7,14,0,0,488,489,7,8,0,0,489,490,7,12,0,0,490,491,7,9,0,0,491,492,7,0,0, - 0,492,493,1,0,0,0,493,494,6,9,5,0,494,34,1,0,0,0,495,496,7,6,0,0,496,497, - 7,3,0,0,497,498,7,9,0,0,498,499,7,12,0,0,499,500,7,16,0,0,500,501,7,3,0, - 0,501,502,1,0,0,0,502,503,6,10,6,0,503,36,1,0,0,0,504,505,7,6,0,0,505,506, - 7,7,0,0,506,507,7,19,0,0,507,508,1,0,0,0,508,509,6,11,0,0,509,38,1,0,0, - 0,510,511,7,2,0,0,511,512,7,10,0,0,512,513,7,7,0,0,513,514,7,19,0,0,514, - 515,1,0,0,0,515,516,6,12,7,0,516,40,1,0,0,0,517,518,7,2,0,0,518,519,7,7, - 0,0,519,520,7,6,0,0,520,521,7,5,0,0,521,522,1,0,0,0,522,523,6,13,0,0,523, - 42,1,0,0,0,524,525,7,2,0,0,525,526,7,5,0,0,526,527,7,12,0,0,527,528,7,5, - 0,0,528,529,7,2,0,0,529,530,1,0,0,0,530,531,6,14,0,0,531,44,1,0,0,0,532, - 533,7,19,0,0,533,534,7,10,0,0,534,535,7,3,0,0,535,536,7,6,0,0,536,537,7, - 3,0,0,537,538,1,0,0,0,538,539,6,15,0,0,539,46,1,0,0,0,540,541,4,16,0,0, - 541,542,7,1,0,0,542,543,7,9,0,0,543,544,7,13,0,0,544,545,7,1,0,0,545,546, - 7,9,0,0,546,547,7,3,0,0,547,548,7,2,0,0,548,549,7,5,0,0,549,550,7,12,0, - 0,550,551,7,5,0,0,551,552,7,2,0,0,552,553,1,0,0,0,553,554,6,16,0,0,554, - 48,1,0,0,0,555,556,4,17,1,0,556,557,7,13,0,0,557,558,7,7,0,0,558,559,7, - 7,0,0,559,560,7,18,0,0,560,561,7,20,0,0,561,562,7,8,0,0,562,563,1,0,0,0, - 563,564,6,17,8,0,564,50,1,0,0,0,565,566,4,18,2,0,566,567,7,16,0,0,567,568, - 7,3,0,0,568,569,7,5,0,0,569,570,7,6,0,0,570,571,7,1,0,0,571,572,7,4,0,0, - 572,573,7,2,0,0,573,574,1,0,0,0,574,575,6,18,9,0,575,52,1,0,0,0,576,578, - 8,21,0,0,577,576,1,0,0,0,578,579,1,0,0,0,579,577,1,0,0,0,579,580,1,0,0, - 0,580,581,1,0,0,0,581,582,6,19,0,0,582,54,1,0,0,0,583,584,5,47,0,0,584, - 585,5,47,0,0,585,589,1,0,0,0,586,588,8,22,0,0,587,586,1,0,0,0,588,591,1, - 0,0,0,589,587,1,0,0,0,589,590,1,0,0,0,590,593,1,0,0,0,591,589,1,0,0,0,592, - 594,5,13,0,0,593,592,1,0,0,0,593,594,1,0,0,0,594,596,1,0,0,0,595,597,5, - 10,0,0,596,595,1,0,0,0,596,597,1,0,0,0,597,598,1,0,0,0,598,599,6,20,10, - 0,599,56,1,0,0,0,600,601,5,47,0,0,601,602,5,42,0,0,602,607,1,0,0,0,603, - 606,3,57,21,0,604,606,9,0,0,0,605,603,1,0,0,0,605,604,1,0,0,0,606,609,1, - 0,0,0,607,608,1,0,0,0,607,605,1,0,0,0,608,610,1,0,0,0,609,607,1,0,0,0,610, - 611,5,42,0,0,611,612,5,47,0,0,612,613,1,0,0,0,613,614,6,21,10,0,614,58, - 1,0,0,0,615,617,7,23,0,0,616,615,1,0,0,0,617,618,1,0,0,0,618,616,1,0,0, - 0,618,619,1,0,0,0,619,620,1,0,0,0,620,621,6,22,10,0,621,60,1,0,0,0,622, - 623,5,124,0,0,623,624,1,0,0,0,624,625,6,23,11,0,625,62,1,0,0,0,626,627, - 7,24,0,0,627,64,1,0,0,0,628,629,7,25,0,0,629,66,1,0,0,0,630,631,5,92,0, - 0,631,632,7,26,0,0,632,68,1,0,0,0,633,634,8,27,0,0,634,70,1,0,0,0,635,637, - 7,3,0,0,636,638,7,28,0,0,637,636,1,0,0,0,637,638,1,0,0,0,638,640,1,0,0, - 0,639,641,3,63,24,0,640,639,1,0,0,0,641,642,1,0,0,0,642,640,1,0,0,0,642, - 643,1,0,0,0,643,72,1,0,0,0,644,645,5,64,0,0,645,74,1,0,0,0,646,647,5,96, - 0,0,647,76,1,0,0,0,648,652,8,29,0,0,649,650,5,96,0,0,650,652,5,96,0,0,651, - 648,1,0,0,0,651,649,1,0,0,0,652,78,1,0,0,0,653,654,5,95,0,0,654,80,1,0, - 0,0,655,659,3,65,25,0,656,659,3,63,24,0,657,659,3,79,32,0,658,655,1,0,0, - 0,658,656,1,0,0,0,658,657,1,0,0,0,659,82,1,0,0,0,660,665,5,34,0,0,661,664, - 3,67,26,0,662,664,3,69,27,0,663,661,1,0,0,0,663,662,1,0,0,0,664,667,1,0, - 0,0,665,663,1,0,0,0,665,666,1,0,0,0,666,668,1,0,0,0,667,665,1,0,0,0,668, - 690,5,34,0,0,669,670,5,34,0,0,670,671,5,34,0,0,671,672,5,34,0,0,672,676, - 1,0,0,0,673,675,8,22,0,0,674,673,1,0,0,0,675,678,1,0,0,0,676,677,1,0,0, - 0,676,674,1,0,0,0,677,679,1,0,0,0,678,676,1,0,0,0,679,680,5,34,0,0,680, - 681,5,34,0,0,681,682,5,34,0,0,682,684,1,0,0,0,683,685,5,34,0,0,684,683, - 1,0,0,0,684,685,1,0,0,0,685,687,1,0,0,0,686,688,5,34,0,0,687,686,1,0,0, - 0,687,688,1,0,0,0,688,690,1,0,0,0,689,660,1,0,0,0,689,669,1,0,0,0,690,84, - 1,0,0,0,691,693,3,63,24,0,692,691,1,0,0,0,693,694,1,0,0,0,694,692,1,0,0, - 0,694,695,1,0,0,0,695,86,1,0,0,0,696,698,3,63,24,0,697,696,1,0,0,0,698, - 699,1,0,0,0,699,697,1,0,0,0,699,700,1,0,0,0,700,701,1,0,0,0,701,705,3,103, - 44,0,702,704,3,63,24,0,703,702,1,0,0,0,704,707,1,0,0,0,705,703,1,0,0,0, - 705,706,1,0,0,0,706,739,1,0,0,0,707,705,1,0,0,0,708,710,3,103,44,0,709, - 711,3,63,24,0,710,709,1,0,0,0,711,712,1,0,0,0,712,710,1,0,0,0,712,713,1, - 0,0,0,713,739,1,0,0,0,714,716,3,63,24,0,715,714,1,0,0,0,716,717,1,0,0,0, - 717,715,1,0,0,0,717,718,1,0,0,0,718,726,1,0,0,0,719,723,3,103,44,0,720, - 722,3,63,24,0,721,720,1,0,0,0,722,725,1,0,0,0,723,721,1,0,0,0,723,724,1, - 0,0,0,724,727,1,0,0,0,725,723,1,0,0,0,726,719,1,0,0,0,726,727,1,0,0,0,727, - 728,1,0,0,0,728,729,3,71,28,0,729,739,1,0,0,0,730,732,3,103,44,0,731,733, - 3,63,24,0,732,731,1,0,0,0,733,734,1,0,0,0,734,732,1,0,0,0,734,735,1,0,0, - 0,735,736,1,0,0,0,736,737,3,71,28,0,737,739,1,0,0,0,738,697,1,0,0,0,738, - 708,1,0,0,0,738,715,1,0,0,0,738,730,1,0,0,0,739,88,1,0,0,0,740,741,7,30, - 0,0,741,742,7,31,0,0,742,90,1,0,0,0,743,744,7,12,0,0,744,745,7,9,0,0,745, - 746,7,0,0,0,746,92,1,0,0,0,747,748,7,12,0,0,748,749,7,2,0,0,749,750,7,4, - 0,0,750,94,1,0,0,0,751,752,5,61,0,0,752,96,1,0,0,0,753,754,5,58,0,0,754, - 755,5,58,0,0,755,98,1,0,0,0,756,757,5,44,0,0,757,100,1,0,0,0,758,759,7, - 0,0,0,759,760,7,3,0,0,760,761,7,2,0,0,761,762,7,4,0,0,762,102,1,0,0,0,763, - 764,5,46,0,0,764,104,1,0,0,0,765,766,7,15,0,0,766,767,7,12,0,0,767,768, - 7,13,0,0,768,769,7,2,0,0,769,770,7,3,0,0,770,106,1,0,0,0,771,772,7,15,0, - 0,772,773,7,1,0,0,773,774,7,6,0,0,774,775,7,2,0,0,775,776,7,5,0,0,776,108, - 1,0,0,0,777,778,7,1,0,0,778,779,7,9,0,0,779,110,1,0,0,0,780,781,7,1,0,0, - 781,782,7,2,0,0,782,112,1,0,0,0,783,784,7,13,0,0,784,785,7,12,0,0,785,786, - 7,2,0,0,786,787,7,5,0,0,787,114,1,0,0,0,788,789,7,13,0,0,789,790,7,1,0, - 0,790,791,7,18,0,0,791,792,7,3,0,0,792,116,1,0,0,0,793,794,5,40,0,0,794, - 118,1,0,0,0,795,796,7,9,0,0,796,797,7,7,0,0,797,798,7,5,0,0,798,120,1,0, - 0,0,799,800,7,9,0,0,800,801,7,20,0,0,801,802,7,13,0,0,802,803,7,13,0,0, - 803,122,1,0,0,0,804,805,7,9,0,0,805,806,7,20,0,0,806,807,7,13,0,0,807,808, - 7,13,0,0,808,809,7,2,0,0,809,124,1,0,0,0,810,811,7,7,0,0,811,812,7,6,0, - 0,812,126,1,0,0,0,813,814,5,63,0,0,814,128,1,0,0,0,815,816,7,6,0,0,816, - 817,7,13,0,0,817,818,7,1,0,0,818,819,7,18,0,0,819,820,7,3,0,0,820,130,1, - 0,0,0,821,822,5,41,0,0,822,132,1,0,0,0,823,824,7,5,0,0,824,825,7,6,0,0, - 825,826,7,20,0,0,826,827,7,3,0,0,827,134,1,0,0,0,828,829,5,61,0,0,829,830, - 5,61,0,0,830,136,1,0,0,0,831,832,5,61,0,0,832,833,5,126,0,0,833,138,1,0, - 0,0,834,835,5,33,0,0,835,836,5,61,0,0,836,140,1,0,0,0,837,838,5,60,0,0, - 838,142,1,0,0,0,839,840,5,60,0,0,840,841,5,61,0,0,841,144,1,0,0,0,842,843, - 5,62,0,0,843,146,1,0,0,0,844,845,5,62,0,0,845,846,5,61,0,0,846,148,1,0, - 0,0,847,848,5,43,0,0,848,150,1,0,0,0,849,850,5,45,0,0,850,152,1,0,0,0,851, - 852,5,42,0,0,852,154,1,0,0,0,853,854,5,47,0,0,854,156,1,0,0,0,855,856,5, - 37,0,0,856,158,1,0,0,0,857,858,7,16,0,0,858,859,7,12,0,0,859,860,7,5,0, - 0,860,861,7,4,0,0,861,862,7,10,0,0,862,160,1,0,0,0,863,864,3,45,15,0,864, - 865,1,0,0,0,865,866,6,73,12,0,866,162,1,0,0,0,867,870,3,127,56,0,868,871, - 3,65,25,0,869,871,3,79,32,0,870,868,1,0,0,0,870,869,1,0,0,0,871,875,1,0, - 0,0,872,874,3,81,33,0,873,872,1,0,0,0,874,877,1,0,0,0,875,873,1,0,0,0,875, - 876,1,0,0,0,876,885,1,0,0,0,877,875,1,0,0,0,878,880,3,127,56,0,879,881, - 3,63,24,0,880,879,1,0,0,0,881,882,1,0,0,0,882,880,1,0,0,0,882,883,1,0,0, - 0,883,885,1,0,0,0,884,867,1,0,0,0,884,878,1,0,0,0,885,164,1,0,0,0,886,887, - 5,91,0,0,887,888,1,0,0,0,888,889,6,75,0,0,889,890,6,75,0,0,890,166,1,0, - 0,0,891,892,5,93,0,0,892,893,1,0,0,0,893,894,6,76,11,0,894,895,6,76,11, - 0,895,168,1,0,0,0,896,900,3,65,25,0,897,899,3,81,33,0,898,897,1,0,0,0,899, - 902,1,0,0,0,900,898,1,0,0,0,900,901,1,0,0,0,901,913,1,0,0,0,902,900,1,0, - 0,0,903,906,3,79,32,0,904,906,3,73,29,0,905,903,1,0,0,0,905,904,1,0,0,0, - 906,908,1,0,0,0,907,909,3,81,33,0,908,907,1,0,0,0,909,910,1,0,0,0,910,908, - 1,0,0,0,910,911,1,0,0,0,911,913,1,0,0,0,912,896,1,0,0,0,912,905,1,0,0,0, - 913,170,1,0,0,0,914,916,3,75,30,0,915,917,3,77,31,0,916,915,1,0,0,0,917, - 918,1,0,0,0,918,916,1,0,0,0,918,919,1,0,0,0,919,920,1,0,0,0,920,921,3,75, - 30,0,921,172,1,0,0,0,922,923,3,171,78,0,923,174,1,0,0,0,924,925,3,55,20, - 0,925,926,1,0,0,0,926,927,6,80,10,0,927,176,1,0,0,0,928,929,3,57,21,0,929, - 930,1,0,0,0,930,931,6,81,10,0,931,178,1,0,0,0,932,933,3,59,22,0,933,934, - 1,0,0,0,934,935,6,82,10,0,935,180,1,0,0,0,936,937,3,165,75,0,937,938,1, - 0,0,0,938,939,6,83,13,0,939,940,6,83,14,0,940,182,1,0,0,0,941,942,3,61, - 23,0,942,943,1,0,0,0,943,944,6,84,15,0,944,945,6,84,11,0,945,184,1,0,0, - 0,946,947,3,59,22,0,947,948,1,0,0,0,948,949,6,85,10,0,949,186,1,0,0,0,950, - 951,3,55,20,0,951,952,1,0,0,0,952,953,6,86,10,0,953,188,1,0,0,0,954,955, - 3,57,21,0,955,956,1,0,0,0,956,957,6,87,10,0,957,190,1,0,0,0,958,959,3,61, - 23,0,959,960,1,0,0,0,960,961,6,88,15,0,961,962,6,88,11,0,962,192,1,0,0, - 0,963,964,3,165,75,0,964,965,1,0,0,0,965,966,6,89,13,0,966,194,1,0,0,0, - 967,968,3,167,76,0,968,969,1,0,0,0,969,970,6,90,16,0,970,196,1,0,0,0,971, - 972,3,337,161,0,972,973,1,0,0,0,973,974,6,91,17,0,974,198,1,0,0,0,975,976, - 3,99,42,0,976,977,1,0,0,0,977,978,6,92,18,0,978,200,1,0,0,0,979,980,3,95, - 40,0,980,981,1,0,0,0,981,982,6,93,19,0,982,202,1,0,0,0,983,984,7,16,0,0, - 984,985,7,3,0,0,985,986,7,5,0,0,986,987,7,12,0,0,987,988,7,0,0,0,988,989, - 7,12,0,0,989,990,7,5,0,0,990,991,7,12,0,0,991,204,1,0,0,0,992,996,8,32, - 0,0,993,994,5,47,0,0,994,996,8,33,0,0,995,992,1,0,0,0,995,993,1,0,0,0,996, - 206,1,0,0,0,997,999,3,205,95,0,998,997,1,0,0,0,999,1000,1,0,0,0,1000,998, - 1,0,0,0,1000,1001,1,0,0,0,1001,208,1,0,0,0,1002,1003,3,207,96,0,1003,1004, - 1,0,0,0,1004,1005,6,97,20,0,1005,210,1,0,0,0,1006,1007,3,83,34,0,1007,1008, - 1,0,0,0,1008,1009,6,98,21,0,1009,212,1,0,0,0,1010,1011,3,55,20,0,1011,1012, - 1,0,0,0,1012,1013,6,99,10,0,1013,214,1,0,0,0,1014,1015,3,57,21,0,1015,1016, - 1,0,0,0,1016,1017,6,100,10,0,1017,216,1,0,0,0,1018,1019,3,59,22,0,1019, - 1020,1,0,0,0,1020,1021,6,101,10,0,1021,218,1,0,0,0,1022,1023,3,61,23,0, - 1023,1024,1,0,0,0,1024,1025,6,102,15,0,1025,1026,6,102,11,0,1026,220,1, - 0,0,0,1027,1028,3,103,44,0,1028,1029,1,0,0,0,1029,1030,6,103,22,0,1030, - 222,1,0,0,0,1031,1032,3,99,42,0,1032,1033,1,0,0,0,1033,1034,6,104,18,0, - 1034,224,1,0,0,0,1035,1036,4,105,3,0,1036,1037,3,127,56,0,1037,1038,1,0, - 0,0,1038,1039,6,105,23,0,1039,226,1,0,0,0,1040,1041,4,106,4,0,1041,1042, - 3,163,74,0,1042,1043,1,0,0,0,1043,1044,6,106,24,0,1044,228,1,0,0,0,1045, - 1050,3,65,25,0,1046,1050,3,63,24,0,1047,1050,3,79,32,0,1048,1050,3,153, - 69,0,1049,1045,1,0,0,0,1049,1046,1,0,0,0,1049,1047,1,0,0,0,1049,1048,1, - 0,0,0,1050,230,1,0,0,0,1051,1054,3,65,25,0,1052,1054,3,153,69,0,1053,1051, - 1,0,0,0,1053,1052,1,0,0,0,1054,1058,1,0,0,0,1055,1057,3,229,107,0,1056, - 1055,1,0,0,0,1057,1060,1,0,0,0,1058,1056,1,0,0,0,1058,1059,1,0,0,0,1059, - 1071,1,0,0,0,1060,1058,1,0,0,0,1061,1064,3,79,32,0,1062,1064,3,73,29,0, - 1063,1061,1,0,0,0,1063,1062,1,0,0,0,1064,1066,1,0,0,0,1065,1067,3,229,107, - 0,1066,1065,1,0,0,0,1067,1068,1,0,0,0,1068,1066,1,0,0,0,1068,1069,1,0,0, - 0,1069,1071,1,0,0,0,1070,1053,1,0,0,0,1070,1063,1,0,0,0,1071,232,1,0,0, - 0,1072,1075,3,231,108,0,1073,1075,3,171,78,0,1074,1072,1,0,0,0,1074,1073, - 1,0,0,0,1075,1076,1,0,0,0,1076,1074,1,0,0,0,1076,1077,1,0,0,0,1077,234, - 1,0,0,0,1078,1079,3,55,20,0,1079,1080,1,0,0,0,1080,1081,6,110,10,0,1081, - 236,1,0,0,0,1082,1083,3,57,21,0,1083,1084,1,0,0,0,1084,1085,6,111,10,0, - 1085,238,1,0,0,0,1086,1087,3,59,22,0,1087,1088,1,0,0,0,1088,1089,6,112, - 10,0,1089,240,1,0,0,0,1090,1091,3,61,23,0,1091,1092,1,0,0,0,1092,1093,6, - 113,15,0,1093,1094,6,113,11,0,1094,242,1,0,0,0,1095,1096,3,95,40,0,1096, - 1097,1,0,0,0,1097,1098,6,114,19,0,1098,244,1,0,0,0,1099,1100,3,99,42,0, - 1100,1101,1,0,0,0,1101,1102,6,115,18,0,1102,246,1,0,0,0,1103,1104,3,103, - 44,0,1104,1105,1,0,0,0,1105,1106,6,116,22,0,1106,248,1,0,0,0,1107,1108, - 4,117,5,0,1108,1109,3,127,56,0,1109,1110,1,0,0,0,1110,1111,6,117,23,0,1111, - 250,1,0,0,0,1112,1113,4,118,6,0,1113,1114,3,163,74,0,1114,1115,1,0,0,0, - 1115,1116,6,118,24,0,1116,252,1,0,0,0,1117,1118,7,12,0,0,1118,1119,7,2, - 0,0,1119,254,1,0,0,0,1120,1121,3,233,109,0,1121,1122,1,0,0,0,1122,1123, - 6,120,25,0,1123,256,1,0,0,0,1124,1125,3,55,20,0,1125,1126,1,0,0,0,1126, - 1127,6,121,10,0,1127,258,1,0,0,0,1128,1129,3,57,21,0,1129,1130,1,0,0,0, - 1130,1131,6,122,10,0,1131,260,1,0,0,0,1132,1133,3,59,22,0,1133,1134,1,0, - 0,0,1134,1135,6,123,10,0,1135,262,1,0,0,0,1136,1137,3,61,23,0,1137,1138, - 1,0,0,0,1138,1139,6,124,15,0,1139,1140,6,124,11,0,1140,264,1,0,0,0,1141, - 1142,3,165,75,0,1142,1143,1,0,0,0,1143,1144,6,125,13,0,1144,1145,6,125, - 26,0,1145,266,1,0,0,0,1146,1147,7,7,0,0,1147,1148,7,9,0,0,1148,1149,1,0, - 0,0,1149,1150,6,126,27,0,1150,268,1,0,0,0,1151,1152,7,19,0,0,1152,1153, - 7,1,0,0,1153,1154,7,5,0,0,1154,1155,7,10,0,0,1155,1156,1,0,0,0,1156,1157, - 6,127,27,0,1157,270,1,0,0,0,1158,1159,8,34,0,0,1159,272,1,0,0,0,1160,1162, - 3,271,128,0,1161,1160,1,0,0,0,1162,1163,1,0,0,0,1163,1161,1,0,0,0,1163, - 1164,1,0,0,0,1164,1165,1,0,0,0,1165,1166,3,337,161,0,1166,1168,1,0,0,0, - 1167,1161,1,0,0,0,1167,1168,1,0,0,0,1168,1170,1,0,0,0,1169,1171,3,271,128, - 0,1170,1169,1,0,0,0,1171,1172,1,0,0,0,1172,1170,1,0,0,0,1172,1173,1,0,0, - 0,1173,274,1,0,0,0,1174,1175,3,273,129,0,1175,1176,1,0,0,0,1176,1177,6, - 130,28,0,1177,276,1,0,0,0,1178,1179,3,55,20,0,1179,1180,1,0,0,0,1180,1181, - 6,131,10,0,1181,278,1,0,0,0,1182,1183,3,57,21,0,1183,1184,1,0,0,0,1184, - 1185,6,132,10,0,1185,280,1,0,0,0,1186,1187,3,59,22,0,1187,1188,1,0,0,0, - 1188,1189,6,133,10,0,1189,282,1,0,0,0,1190,1191,3,61,23,0,1191,1192,1,0, - 0,0,1192,1193,6,134,15,0,1193,1194,6,134,11,0,1194,1195,6,134,11,0,1195, - 284,1,0,0,0,1196,1197,3,95,40,0,1197,1198,1,0,0,0,1198,1199,6,135,19,0, - 1199,286,1,0,0,0,1200,1201,3,99,42,0,1201,1202,1,0,0,0,1202,1203,6,136, - 18,0,1203,288,1,0,0,0,1204,1205,3,103,44,0,1205,1206,1,0,0,0,1206,1207, - 6,137,22,0,1207,290,1,0,0,0,1208,1209,3,269,127,0,1209,1210,1,0,0,0,1210, - 1211,6,138,29,0,1211,292,1,0,0,0,1212,1213,3,233,109,0,1213,1214,1,0,0, - 0,1214,1215,6,139,25,0,1215,294,1,0,0,0,1216,1217,3,173,79,0,1217,1218, - 1,0,0,0,1218,1219,6,140,30,0,1219,296,1,0,0,0,1220,1221,4,141,7,0,1221, - 1222,3,127,56,0,1222,1223,1,0,0,0,1223,1224,6,141,23,0,1224,298,1,0,0,0, - 1225,1226,4,142,8,0,1226,1227,3,163,74,0,1227,1228,1,0,0,0,1228,1229,6, - 142,24,0,1229,300,1,0,0,0,1230,1231,3,55,20,0,1231,1232,1,0,0,0,1232,1233, - 6,143,10,0,1233,302,1,0,0,0,1234,1235,3,57,21,0,1235,1236,1,0,0,0,1236, - 1237,6,144,10,0,1237,304,1,0,0,0,1238,1239,3,59,22,0,1239,1240,1,0,0,0, - 1240,1241,6,145,10,0,1241,306,1,0,0,0,1242,1243,3,61,23,0,1243,1244,1,0, - 0,0,1244,1245,6,146,15,0,1245,1246,6,146,11,0,1246,308,1,0,0,0,1247,1248, - 3,103,44,0,1248,1249,1,0,0,0,1249,1250,6,147,22,0,1250,310,1,0,0,0,1251, - 1252,4,148,9,0,1252,1253,3,127,56,0,1253,1254,1,0,0,0,1254,1255,6,148,23, - 0,1255,312,1,0,0,0,1256,1257,4,149,10,0,1257,1258,3,163,74,0,1258,1259, - 1,0,0,0,1259,1260,6,149,24,0,1260,314,1,0,0,0,1261,1262,3,173,79,0,1262, - 1263,1,0,0,0,1263,1264,6,150,30,0,1264,316,1,0,0,0,1265,1266,3,169,77,0, - 1266,1267,1,0,0,0,1267,1268,6,151,31,0,1268,318,1,0,0,0,1269,1270,3,55, - 20,0,1270,1271,1,0,0,0,1271,1272,6,152,10,0,1272,320,1,0,0,0,1273,1274, - 3,57,21,0,1274,1275,1,0,0,0,1275,1276,6,153,10,0,1276,322,1,0,0,0,1277, - 1278,3,59,22,0,1278,1279,1,0,0,0,1279,1280,6,154,10,0,1280,324,1,0,0,0, - 1281,1282,3,61,23,0,1282,1283,1,0,0,0,1283,1284,6,155,15,0,1284,1285,6, - 155,11,0,1285,326,1,0,0,0,1286,1287,7,1,0,0,1287,1288,7,9,0,0,1288,1289, - 7,15,0,0,1289,1290,7,7,0,0,1290,328,1,0,0,0,1291,1292,3,55,20,0,1292,1293, - 1,0,0,0,1293,1294,6,157,10,0,1294,330,1,0,0,0,1295,1296,3,57,21,0,1296, - 1297,1,0,0,0,1297,1298,6,158,10,0,1298,332,1,0,0,0,1299,1300,3,59,22,0, - 1300,1301,1,0,0,0,1301,1302,6,159,10,0,1302,334,1,0,0,0,1303,1304,3,167, - 76,0,1304,1305,1,0,0,0,1305,1306,6,160,16,0,1306,1307,6,160,11,0,1307,336, - 1,0,0,0,1308,1309,5,58,0,0,1309,338,1,0,0,0,1310,1316,3,73,29,0,1311,1316, - 3,63,24,0,1312,1316,3,103,44,0,1313,1316,3,65,25,0,1314,1316,3,79,32,0, - 1315,1310,1,0,0,0,1315,1311,1,0,0,0,1315,1312,1,0,0,0,1315,1313,1,0,0,0, - 1315,1314,1,0,0,0,1316,1317,1,0,0,0,1317,1315,1,0,0,0,1317,1318,1,0,0,0, - 1318,340,1,0,0,0,1319,1320,3,55,20,0,1320,1321,1,0,0,0,1321,1322,6,163, - 10,0,1322,342,1,0,0,0,1323,1324,3,57,21,0,1324,1325,1,0,0,0,1325,1326,6, - 164,10,0,1326,344,1,0,0,0,1327,1328,3,59,22,0,1328,1329,1,0,0,0,1329,1330, - 6,165,10,0,1330,346,1,0,0,0,1331,1332,3,61,23,0,1332,1333,1,0,0,0,1333, - 1334,6,166,15,0,1334,1335,6,166,11,0,1335,348,1,0,0,0,1336,1337,3,337,161, - 0,1337,1338,1,0,0,0,1338,1339,6,167,17,0,1339,350,1,0,0,0,1340,1341,3,99, - 42,0,1341,1342,1,0,0,0,1342,1343,6,168,18,0,1343,352,1,0,0,0,1344,1345, - 3,103,44,0,1345,1346,1,0,0,0,1346,1347,6,169,22,0,1347,354,1,0,0,0,1348, - 1349,3,267,126,0,1349,1350,1,0,0,0,1350,1351,6,170,32,0,1351,1352,6,170, - 33,0,1352,356,1,0,0,0,1353,1354,3,207,96,0,1354,1355,1,0,0,0,1355,1356, - 6,171,20,0,1356,358,1,0,0,0,1357,1358,3,83,34,0,1358,1359,1,0,0,0,1359, - 1360,6,172,21,0,1360,360,1,0,0,0,1361,1362,3,55,20,0,1362,1363,1,0,0,0, - 1363,1364,6,173,10,0,1364,362,1,0,0,0,1365,1366,3,57,21,0,1366,1367,1,0, - 0,0,1367,1368,6,174,10,0,1368,364,1,0,0,0,1369,1370,3,59,22,0,1370,1371, - 1,0,0,0,1371,1372,6,175,10,0,1372,366,1,0,0,0,1373,1374,3,61,23,0,1374, - 1375,1,0,0,0,1375,1376,6,176,15,0,1376,1377,6,176,11,0,1377,1378,6,176, - 11,0,1378,368,1,0,0,0,1379,1380,3,99,42,0,1380,1381,1,0,0,0,1381,1382,6, - 177,18,0,1382,370,1,0,0,0,1383,1384,3,103,44,0,1384,1385,1,0,0,0,1385,1386, - 6,178,22,0,1386,372,1,0,0,0,1387,1388,3,233,109,0,1388,1389,1,0,0,0,1389, - 1390,6,179,25,0,1390,374,1,0,0,0,1391,1392,3,55,20,0,1392,1393,1,0,0,0, - 1393,1394,6,180,10,0,1394,376,1,0,0,0,1395,1396,3,57,21,0,1396,1397,1,0, - 0,0,1397,1398,6,181,10,0,1398,378,1,0,0,0,1399,1400,3,59,22,0,1400,1401, - 1,0,0,0,1401,1402,6,182,10,0,1402,380,1,0,0,0,1403,1404,3,61,23,0,1404, - 1405,1,0,0,0,1405,1406,6,183,15,0,1406,1407,6,183,11,0,1407,382,1,0,0,0, - 1408,1409,3,207,96,0,1409,1410,1,0,0,0,1410,1411,6,184,20,0,1411,1412,6, - 184,11,0,1412,1413,6,184,34,0,1413,384,1,0,0,0,1414,1415,3,83,34,0,1415, - 1416,1,0,0,0,1416,1417,6,185,21,0,1417,1418,6,185,11,0,1418,1419,6,185, - 34,0,1419,386,1,0,0,0,1420,1421,3,55,20,0,1421,1422,1,0,0,0,1422,1423,6, - 186,10,0,1423,388,1,0,0,0,1424,1425,3,57,21,0,1425,1426,1,0,0,0,1426,1427, - 6,187,10,0,1427,390,1,0,0,0,1428,1429,3,59,22,0,1429,1430,1,0,0,0,1430, - 1431,6,188,10,0,1431,392,1,0,0,0,1432,1433,3,337,161,0,1433,1434,1,0,0, - 0,1434,1435,6,189,17,0,1435,1436,6,189,11,0,1436,1437,6,189,9,0,1437,394, - 1,0,0,0,1438,1439,3,99,42,0,1439,1440,1,0,0,0,1440,1441,6,190,18,0,1441, - 1442,6,190,11,0,1442,1443,6,190,9,0,1443,396,1,0,0,0,1444,1445,3,55,20, - 0,1445,1446,1,0,0,0,1446,1447,6,191,10,0,1447,398,1,0,0,0,1448,1449,3,57, - 21,0,1449,1450,1,0,0,0,1450,1451,6,192,10,0,1451,400,1,0,0,0,1452,1453, - 3,59,22,0,1453,1454,1,0,0,0,1454,1455,6,193,10,0,1455,402,1,0,0,0,1456, - 1457,3,173,79,0,1457,1458,1,0,0,0,1458,1459,6,194,11,0,1459,1460,6,194, - 0,0,1460,1461,6,194,30,0,1461,404,1,0,0,0,1462,1463,3,169,77,0,1463,1464, - 1,0,0,0,1464,1465,6,195,11,0,1465,1466,6,195,0,0,1466,1467,6,195,31,0,1467, - 406,1,0,0,0,1468,1469,3,89,37,0,1469,1470,1,0,0,0,1470,1471,6,196,11,0, - 1471,1472,6,196,0,0,1472,1473,6,196,35,0,1473,408,1,0,0,0,1474,1475,3,61, - 23,0,1475,1476,1,0,0,0,1476,1477,6,197,15,0,1477,1478,6,197,11,0,1478,410, - 1,0,0,0,65,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,579,589,593,596,605,607,618, - 637,642,651,658,663,665,676,684,687,689,694,699,705,712,717,723,726,734, - 738,870,875,882,884,900,905,910,912,918,995,1000,1049,1053,1058,1063,1068, - 1070,1074,1076,1163,1167,1172,1315,1317,36,5,1,0,5,4,0,5,6,0,5,2,0,5,3, - 0,5,8,0,5,5,0,5,9,0,5,11,0,5,13,0,0,1,0,4,0,0,7,16,0,7,65,0,5,0,0,7,24, - 0,7,66,0,7,104,0,7,33,0,7,31,0,7,76,0,7,25,0,7,35,0,7,47,0,7,64,0,7,80, - 0,5,10,0,5,7,0,7,90,0,7,89,0,7,68,0,7,67,0,7,88,0,5,12,0,5,14,0,7,28,0]; + 2,193,7,193,2,194,7,194,2,195,7,195,2,196,7,196,2,197,7,197,2,198,7,198, + 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2, + 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4, + 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6, + 1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8, + 1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10, + 1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1, + 12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14, + 1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1, + 16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17, + 1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,18,1,18,1,19,4,19,580,8,19,11,19,12,19,581,1,19,1,19,1,20,1,20, + 1,20,1,20,5,20,590,8,20,10,20,12,20,593,9,20,1,20,3,20,596,8,20,1,20,3, + 20,599,8,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21,5,21,608,8,21,10,21,12,21, + 611,9,21,1,21,1,21,1,21,1,21,1,21,1,22,4,22,619,8,22,11,22,12,22,620,1, + 22,1,22,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1,25,1,26,1,26,1,27,1,27,1,27, + 1,28,1,28,1,29,1,29,3,29,642,8,29,1,29,4,29,645,8,29,11,29,12,29,646,1, + 30,1,30,1,31,1,31,1,32,1,32,1,32,3,32,656,8,32,1,33,1,33,1,34,1,34,1,34, + 3,34,663,8,34,1,35,1,35,1,35,5,35,668,8,35,10,35,12,35,671,9,35,1,35,1, + 35,1,35,1,35,1,35,1,35,5,35,679,8,35,10,35,12,35,682,9,35,1,35,1,35,1,35, + 1,35,1,35,3,35,689,8,35,1,35,3,35,692,8,35,3,35,694,8,35,1,36,4,36,697, + 8,36,11,36,12,36,698,1,37,4,37,702,8,37,11,37,12,37,703,1,37,1,37,5,37, + 708,8,37,10,37,12,37,711,9,37,1,37,1,37,4,37,715,8,37,11,37,12,37,716,1, + 37,4,37,720,8,37,11,37,12,37,721,1,37,1,37,5,37,726,8,37,10,37,12,37,729, + 9,37,3,37,731,8,37,1,37,1,37,1,37,1,37,4,37,737,8,37,11,37,12,37,738,1, + 37,1,37,3,37,743,8,37,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,40,1,40,1,40, + 1,40,1,41,1,41,1,42,1,42,1,42,1,43,1,43,1,44,1,44,1,44,1,44,1,44,1,45,1, + 45,1,46,1,46,1,46,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,48,1,48, + 1,48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1, + 52,1,52,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1,55, + 1,55,1,55,1,56,1,56,1,56,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,59,1, + 59,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,62,1,62,1,62,1,63,1,63,1,63, + 1,64,1,64,1,65,1,65,1,65,1,66,1,66,1,67,1,67,1,67,1,68,1,68,1,69,1,69,1, + 70,1,70,1,71,1,71,1,72,1,72,1,73,1,73,1,73,1,73,1,73,1,74,1,74,1,74,1,74, + 1,75,1,75,1,75,3,75,874,8,75,1,75,5,75,877,8,75,10,75,12,75,880,9,75,1, + 75,1,75,4,75,884,8,75,11,75,12,75,885,3,75,888,8,75,1,76,1,76,1,76,1,76, + 1,76,1,77,1,77,1,77,1,77,1,77,1,78,1,78,5,78,902,8,78,10,78,12,78,905,9, + 78,1,78,1,78,3,78,909,8,78,1,78,4,78,912,8,78,11,78,12,78,913,3,78,916, + 8,78,1,79,1,79,4,79,920,8,79,11,79,12,79,921,1,79,1,79,1,80,1,80,1,81,1, + 81,1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84, + 1,84,1,85,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1, + 88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,91,1,91, + 1,91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1, + 95,1,95,1,95,1,95,1,95,1,95,1,95,1,95,1,95,1,96,1,96,1,96,3,96,999,8,96, + 1,97,4,97,1002,8,97,11,97,12,97,1003,1,98,1,98,1,98,1,98,1,99,1,99,1,99, + 1,99,1,100,1,100,1,100,1,100,1,101,1,101,1,101,1,101,1,102,1,102,1,102, + 1,102,1,103,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,1,105,1,105, + 1,105,1,105,1,106,1,106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,1,107, + 1,108,1,108,1,108,1,108,3,108,1053,8,108,1,109,1,109,3,109,1057,8,109,1, + 109,5,109,1060,8,109,10,109,12,109,1063,9,109,1,109,1,109,3,109,1067,8, + 109,1,109,4,109,1070,8,109,11,109,12,109,1071,3,109,1074,8,109,1,110,1, + 110,4,110,1078,8,110,11,110,12,110,1079,1,111,1,111,1,111,1,111,1,112,1, + 112,1,112,1,112,1,113,1,113,1,113,1,113,1,114,1,114,1,114,1,114,1,114,1, + 115,1,115,1,115,1,115,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1, + 118,1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,119,1,119,1,120,1,120,1, + 120,1,121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123,1, + 123,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1, + 126,1,126,1,126,1,127,1,127,1,127,1,127,1,127,1,128,1,128,1,128,1,128,1, + 128,1,128,1,128,1,129,1,129,1,130,4,130,1165,8,130,11,130,12,130,1166,1, + 130,1,130,3,130,1171,8,130,1,130,4,130,1174,8,130,11,130,12,130,1175,1, + 131,1,131,1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1, + 134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,135,1,135,1,136,1,136,1, + 136,1,136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1, + 139,1,139,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,142,1,142,1, + 142,1,142,1,142,1,143,1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1, + 145,1,145,1,145,1,145,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1, + 147,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149,1,149,1,150,1,150,1, + 150,1,150,1,150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,153,1, + 153,1,153,1,153,1,154,1,154,1,154,1,154,1,155,1,155,1,155,1,155,1,156,1, + 156,1,156,1,156,1,156,1,157,1,157,1,157,1,157,1,157,1,158,1,158,1,158,1, + 158,1,159,1,159,1,159,1,159,1,160,1,160,1,160,1,160,1,161,1,161,1,161,1, + 161,1,161,1,162,1,162,1,162,1,162,1,163,1,163,1,163,1,163,1,163,4,163,1321, + 8,163,11,163,12,163,1322,1,164,1,164,1,164,1,164,1,165,1,165,1,165,1,165, + 1,166,1,166,1,166,1,166,1,167,1,167,1,167,1,167,1,167,1,168,1,168,1,168, + 1,168,1,169,1,169,1,169,1,169,1,170,1,170,1,170,1,170,1,171,1,171,1,171, + 1,171,1,171,1,172,1,172,1,172,1,172,1,173,1,173,1,173,1,173,1,174,1,174, + 1,174,1,174,1,175,1,175,1,175,1,175,1,176,1,176,1,176,1,176,1,177,1,177, + 1,177,1,177,1,177,1,177,1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179, + 1,180,1,180,1,180,1,180,1,181,1,181,1,181,1,181,1,182,1,182,1,182,1,182, + 1,183,1,183,1,183,1,183,1,184,1,184,1,184,1,184,1,184,1,185,1,185,1,185, + 1,185,1,185,1,185,1,186,1,186,1,186,1,186,1,186,1,186,1,187,1,187,1,187, + 1,187,1,188,1,188,1,188,1,188,1,189,1,189,1,189,1,189,1,190,1,190,1,190, + 1,190,1,190,1,190,1,191,1,191,1,191,1,191,1,191,1,191,1,192,1,192,1,192, + 1,192,1,193,1,193,1,193,1,193,1,194,1,194,1,194,1,194,1,195,1,195,1,195, + 1,195,1,195,1,195,1,196,1,196,1,196,1,196,1,196,1,196,1,197,1,197,1,197, + 1,197,1,197,1,197,1,198,1,198,1,198,1,198,1,198,2,609,680,0,199,15,1,17, + 2,19,3,21,4,23,5,25,6,27,7,29,8,31,9,33,10,35,11,37,12,39,13,41,14,43,15, + 45,16,47,17,49,18,51,19,53,20,55,21,57,22,59,23,61,24,63,25,65,0,67,0,69, + 0,71,0,73,0,75,0,77,0,79,0,81,0,83,0,85,26,87,27,89,28,91,29,93,30,95,31, + 97,32,99,33,101,34,103,35,105,36,107,37,109,38,111,39,113,40,115,41,117, + 42,119,43,121,44,123,45,125,46,127,47,129,48,131,49,133,50,135,51,137,52, + 139,53,141,54,143,55,145,56,147,57,149,58,151,59,153,60,155,61,157,62,159, + 63,161,0,163,0,165,64,167,65,169,66,171,67,173,0,175,68,177,69,179,70,181, + 71,183,0,185,0,187,72,189,73,191,74,193,0,195,0,197,0,199,0,201,0,203,0, + 205,75,207,0,209,76,211,0,213,0,215,77,217,78,219,79,221,0,223,0,225,0, + 227,0,229,0,231,0,233,0,235,80,237,81,239,82,241,83,243,0,245,0,247,0,249, + 0,251,0,253,0,255,84,257,0,259,85,261,86,263,87,265,0,267,0,269,88,271, + 89,273,0,275,90,277,0,279,91,281,92,283,93,285,0,287,0,289,0,291,0,293, + 0,295,0,297,0,299,0,301,0,303,94,305,95,307,96,309,0,311,0,313,0,315,0, + 317,0,319,0,321,97,323,98,325,99,327,0,329,100,331,101,333,102,335,103, + 337,0,339,0,341,104,343,105,345,106,347,107,349,0,351,0,353,0,355,0,357, + 0,359,0,361,0,363,108,365,109,367,110,369,0,371,0,373,0,375,0,377,111,379, + 112,381,113,383,0,385,0,387,0,389,114,391,115,393,116,395,0,397,0,399,117, + 401,118,403,119,405,0,407,0,409,0,411,0,15,0,1,2,3,4,5,6,7,8,9,10,11,12, + 13,14,35,2,0,68,68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69, + 101,101,2,0,67,67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111, + 111,2,0,80,80,112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118, + 118,2,0,65,65,97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102, + 2,0,77,77,109,109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,87,87,119,119, + 2,0,85,85,117,117,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13, + 3,0,9,10,13,13,32,32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84, + 84,92,92,110,110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43, + 45,45,1,0,96,96,2,0,66,66,98,98,2,0,89,89,121,121,11,0,9,10,13,13,32,32, + 34,34,44,44,47,47,58,58,61,61,91,91,93,93,124,124,2,0,42,42,47,47,11,0, + 9,10,13,13,32,32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,1512, + 0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1, + 0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0, + 0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1, + 0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0, + 0,59,1,0,0,0,0,61,1,0,0,0,1,63,1,0,0,0,1,85,1,0,0,0,1,87,1,0,0,0,1,89,1, + 0,0,0,1,91,1,0,0,0,1,93,1,0,0,0,1,95,1,0,0,0,1,97,1,0,0,0,1,99,1,0,0,0, + 1,101,1,0,0,0,1,103,1,0,0,0,1,105,1,0,0,0,1,107,1,0,0,0,1,109,1,0,0,0,1, + 111,1,0,0,0,1,113,1,0,0,0,1,115,1,0,0,0,1,117,1,0,0,0,1,119,1,0,0,0,1,121, + 1,0,0,0,1,123,1,0,0,0,1,125,1,0,0,0,1,127,1,0,0,0,1,129,1,0,0,0,1,131,1, + 0,0,0,1,133,1,0,0,0,1,135,1,0,0,0,1,137,1,0,0,0,1,139,1,0,0,0,1,141,1,0, + 0,0,1,143,1,0,0,0,1,145,1,0,0,0,1,147,1,0,0,0,1,149,1,0,0,0,1,151,1,0,0, + 0,1,153,1,0,0,0,1,155,1,0,0,0,1,157,1,0,0,0,1,159,1,0,0,0,1,161,1,0,0,0, + 1,163,1,0,0,0,1,165,1,0,0,0,1,167,1,0,0,0,1,169,1,0,0,0,1,171,1,0,0,0,1, + 175,1,0,0,0,1,177,1,0,0,0,1,179,1,0,0,0,1,181,1,0,0,0,2,183,1,0,0,0,2,185, + 1,0,0,0,2,187,1,0,0,0,2,189,1,0,0,0,2,191,1,0,0,0,3,193,1,0,0,0,3,195,1, + 0,0,0,3,197,1,0,0,0,3,199,1,0,0,0,3,201,1,0,0,0,3,203,1,0,0,0,3,205,1,0, + 0,0,3,209,1,0,0,0,3,211,1,0,0,0,3,213,1,0,0,0,3,215,1,0,0,0,3,217,1,0,0, + 0,3,219,1,0,0,0,4,221,1,0,0,0,4,223,1,0,0,0,4,225,1,0,0,0,4,227,1,0,0,0, + 4,229,1,0,0,0,4,235,1,0,0,0,4,237,1,0,0,0,4,239,1,0,0,0,4,241,1,0,0,0,5, + 243,1,0,0,0,5,245,1,0,0,0,5,247,1,0,0,0,5,249,1,0,0,0,5,251,1,0,0,0,5,253, + 1,0,0,0,5,255,1,0,0,0,5,257,1,0,0,0,5,259,1,0,0,0,5,261,1,0,0,0,5,263,1, + 0,0,0,6,265,1,0,0,0,6,267,1,0,0,0,6,269,1,0,0,0,6,271,1,0,0,0,6,275,1,0, + 0,0,6,277,1,0,0,0,6,279,1,0,0,0,6,281,1,0,0,0,6,283,1,0,0,0,7,285,1,0,0, + 0,7,287,1,0,0,0,7,289,1,0,0,0,7,291,1,0,0,0,7,293,1,0,0,0,7,295,1,0,0,0, + 7,297,1,0,0,0,7,299,1,0,0,0,7,301,1,0,0,0,7,303,1,0,0,0,7,305,1,0,0,0,7, + 307,1,0,0,0,8,309,1,0,0,0,8,311,1,0,0,0,8,313,1,0,0,0,8,315,1,0,0,0,8,317, + 1,0,0,0,8,319,1,0,0,0,8,321,1,0,0,0,8,323,1,0,0,0,8,325,1,0,0,0,9,327,1, + 0,0,0,9,329,1,0,0,0,9,331,1,0,0,0,9,333,1,0,0,0,9,335,1,0,0,0,10,337,1, + 0,0,0,10,339,1,0,0,0,10,341,1,0,0,0,10,343,1,0,0,0,10,345,1,0,0,0,10,347, + 1,0,0,0,11,349,1,0,0,0,11,351,1,0,0,0,11,353,1,0,0,0,11,355,1,0,0,0,11, + 357,1,0,0,0,11,359,1,0,0,0,11,361,1,0,0,0,11,363,1,0,0,0,11,365,1,0,0,0, + 11,367,1,0,0,0,12,369,1,0,0,0,12,371,1,0,0,0,12,373,1,0,0,0,12,375,1,0, + 0,0,12,377,1,0,0,0,12,379,1,0,0,0,12,381,1,0,0,0,13,383,1,0,0,0,13,385, + 1,0,0,0,13,387,1,0,0,0,13,389,1,0,0,0,13,391,1,0,0,0,13,393,1,0,0,0,14, + 395,1,0,0,0,14,397,1,0,0,0,14,399,1,0,0,0,14,401,1,0,0,0,14,403,1,0,0,0, + 14,405,1,0,0,0,14,407,1,0,0,0,14,409,1,0,0,0,14,411,1,0,0,0,15,413,1,0, + 0,0,17,423,1,0,0,0,19,430,1,0,0,0,21,439,1,0,0,0,23,446,1,0,0,0,25,456, + 1,0,0,0,27,463,1,0,0,0,29,470,1,0,0,0,31,477,1,0,0,0,33,485,1,0,0,0,35, + 497,1,0,0,0,37,506,1,0,0,0,39,512,1,0,0,0,41,519,1,0,0,0,43,526,1,0,0,0, + 45,534,1,0,0,0,47,542,1,0,0,0,49,557,1,0,0,0,51,567,1,0,0,0,53,579,1,0, + 0,0,55,585,1,0,0,0,57,602,1,0,0,0,59,618,1,0,0,0,61,624,1,0,0,0,63,626, + 1,0,0,0,65,630,1,0,0,0,67,632,1,0,0,0,69,634,1,0,0,0,71,637,1,0,0,0,73, + 639,1,0,0,0,75,648,1,0,0,0,77,650,1,0,0,0,79,655,1,0,0,0,81,657,1,0,0,0, + 83,662,1,0,0,0,85,693,1,0,0,0,87,696,1,0,0,0,89,742,1,0,0,0,91,744,1,0, + 0,0,93,747,1,0,0,0,95,751,1,0,0,0,97,755,1,0,0,0,99,757,1,0,0,0,101,760, + 1,0,0,0,103,762,1,0,0,0,105,767,1,0,0,0,107,769,1,0,0,0,109,775,1,0,0,0, + 111,781,1,0,0,0,113,784,1,0,0,0,115,787,1,0,0,0,117,792,1,0,0,0,119,797, + 1,0,0,0,121,799,1,0,0,0,123,803,1,0,0,0,125,808,1,0,0,0,127,814,1,0,0,0, + 129,817,1,0,0,0,131,819,1,0,0,0,133,825,1,0,0,0,135,827,1,0,0,0,137,832, + 1,0,0,0,139,835,1,0,0,0,141,838,1,0,0,0,143,841,1,0,0,0,145,843,1,0,0,0, + 147,846,1,0,0,0,149,848,1,0,0,0,151,851,1,0,0,0,153,853,1,0,0,0,155,855, + 1,0,0,0,157,857,1,0,0,0,159,859,1,0,0,0,161,861,1,0,0,0,163,866,1,0,0,0, + 165,887,1,0,0,0,167,889,1,0,0,0,169,894,1,0,0,0,171,915,1,0,0,0,173,917, + 1,0,0,0,175,925,1,0,0,0,177,927,1,0,0,0,179,931,1,0,0,0,181,935,1,0,0,0, + 183,939,1,0,0,0,185,944,1,0,0,0,187,949,1,0,0,0,189,953,1,0,0,0,191,957, + 1,0,0,0,193,961,1,0,0,0,195,966,1,0,0,0,197,970,1,0,0,0,199,974,1,0,0,0, + 201,978,1,0,0,0,203,982,1,0,0,0,205,986,1,0,0,0,207,998,1,0,0,0,209,1001, + 1,0,0,0,211,1005,1,0,0,0,213,1009,1,0,0,0,215,1013,1,0,0,0,217,1017,1,0, + 0,0,219,1021,1,0,0,0,221,1025,1,0,0,0,223,1030,1,0,0,0,225,1034,1,0,0,0, + 227,1038,1,0,0,0,229,1043,1,0,0,0,231,1052,1,0,0,0,233,1073,1,0,0,0,235, + 1077,1,0,0,0,237,1081,1,0,0,0,239,1085,1,0,0,0,241,1089,1,0,0,0,243,1093, + 1,0,0,0,245,1098,1,0,0,0,247,1102,1,0,0,0,249,1106,1,0,0,0,251,1110,1,0, + 0,0,253,1115,1,0,0,0,255,1120,1,0,0,0,257,1123,1,0,0,0,259,1127,1,0,0,0, + 261,1131,1,0,0,0,263,1135,1,0,0,0,265,1139,1,0,0,0,267,1144,1,0,0,0,269, + 1149,1,0,0,0,271,1154,1,0,0,0,273,1161,1,0,0,0,275,1170,1,0,0,0,277,1177, + 1,0,0,0,279,1181,1,0,0,0,281,1185,1,0,0,0,283,1189,1,0,0,0,285,1193,1,0, + 0,0,287,1199,1,0,0,0,289,1203,1,0,0,0,291,1207,1,0,0,0,293,1211,1,0,0,0, + 295,1215,1,0,0,0,297,1219,1,0,0,0,299,1223,1,0,0,0,301,1228,1,0,0,0,303, + 1233,1,0,0,0,305,1237,1,0,0,0,307,1241,1,0,0,0,309,1245,1,0,0,0,311,1250, + 1,0,0,0,313,1254,1,0,0,0,315,1259,1,0,0,0,317,1264,1,0,0,0,319,1268,1,0, + 0,0,321,1272,1,0,0,0,323,1276,1,0,0,0,325,1280,1,0,0,0,327,1284,1,0,0,0, + 329,1289,1,0,0,0,331,1294,1,0,0,0,333,1298,1,0,0,0,335,1302,1,0,0,0,337, + 1306,1,0,0,0,339,1311,1,0,0,0,341,1320,1,0,0,0,343,1324,1,0,0,0,345,1328, + 1,0,0,0,347,1332,1,0,0,0,349,1336,1,0,0,0,351,1341,1,0,0,0,353,1345,1,0, + 0,0,355,1349,1,0,0,0,357,1353,1,0,0,0,359,1358,1,0,0,0,361,1362,1,0,0,0, + 363,1366,1,0,0,0,365,1370,1,0,0,0,367,1374,1,0,0,0,369,1378,1,0,0,0,371, + 1384,1,0,0,0,373,1388,1,0,0,0,375,1392,1,0,0,0,377,1396,1,0,0,0,379,1400, + 1,0,0,0,381,1404,1,0,0,0,383,1408,1,0,0,0,385,1413,1,0,0,0,387,1419,1,0, + 0,0,389,1425,1,0,0,0,391,1429,1,0,0,0,393,1433,1,0,0,0,395,1437,1,0,0,0, + 397,1443,1,0,0,0,399,1449,1,0,0,0,401,1453,1,0,0,0,403,1457,1,0,0,0,405, + 1461,1,0,0,0,407,1467,1,0,0,0,409,1473,1,0,0,0,411,1479,1,0,0,0,413,414, + 7,0,0,0,414,415,7,1,0,0,415,416,7,2,0,0,416,417,7,2,0,0,417,418,7,3,0,0, + 418,419,7,4,0,0,419,420,7,5,0,0,420,421,1,0,0,0,421,422,6,0,0,0,422,16, + 1,0,0,0,423,424,7,0,0,0,424,425,7,6,0,0,425,426,7,7,0,0,426,427,7,8,0,0, + 427,428,1,0,0,0,428,429,6,1,1,0,429,18,1,0,0,0,430,431,7,3,0,0,431,432, + 7,9,0,0,432,433,7,6,0,0,433,434,7,1,0,0,434,435,7,4,0,0,435,436,7,10,0, + 0,436,437,1,0,0,0,437,438,6,2,2,0,438,20,1,0,0,0,439,440,7,3,0,0,440,441, + 7,11,0,0,441,442,7,12,0,0,442,443,7,13,0,0,443,444,1,0,0,0,444,445,6,3, + 0,0,445,22,1,0,0,0,446,447,7,3,0,0,447,448,7,14,0,0,448,449,7,8,0,0,449, + 450,7,13,0,0,450,451,7,12,0,0,451,452,7,1,0,0,452,453,7,9,0,0,453,454,1, + 0,0,0,454,455,6,4,3,0,455,24,1,0,0,0,456,457,7,15,0,0,457,458,7,6,0,0,458, + 459,7,7,0,0,459,460,7,16,0,0,460,461,1,0,0,0,461,462,6,5,4,0,462,26,1,0, + 0,0,463,464,7,17,0,0,464,465,7,6,0,0,465,466,7,7,0,0,466,467,7,18,0,0,467, + 468,1,0,0,0,468,469,6,6,0,0,469,28,1,0,0,0,470,471,7,18,0,0,471,472,7,3, + 0,0,472,473,7,3,0,0,473,474,7,8,0,0,474,475,1,0,0,0,475,476,6,7,1,0,476, + 30,1,0,0,0,477,478,7,13,0,0,478,479,7,1,0,0,479,480,7,16,0,0,480,481,7, + 1,0,0,481,482,7,5,0,0,482,483,1,0,0,0,483,484,6,8,0,0,484,32,1,0,0,0,485, + 486,7,16,0,0,486,487,7,11,0,0,487,488,5,95,0,0,488,489,7,3,0,0,489,490, + 7,14,0,0,490,491,7,8,0,0,491,492,7,12,0,0,492,493,7,9,0,0,493,494,7,0,0, + 0,494,495,1,0,0,0,495,496,6,9,5,0,496,34,1,0,0,0,497,498,7,6,0,0,498,499, + 7,3,0,0,499,500,7,9,0,0,500,501,7,12,0,0,501,502,7,16,0,0,502,503,7,3,0, + 0,503,504,1,0,0,0,504,505,6,10,6,0,505,36,1,0,0,0,506,507,7,6,0,0,507,508, + 7,7,0,0,508,509,7,19,0,0,509,510,1,0,0,0,510,511,6,11,0,0,511,38,1,0,0, + 0,512,513,7,2,0,0,513,514,7,10,0,0,514,515,7,7,0,0,515,516,7,19,0,0,516, + 517,1,0,0,0,517,518,6,12,7,0,518,40,1,0,0,0,519,520,7,2,0,0,520,521,7,7, + 0,0,521,522,7,6,0,0,522,523,7,5,0,0,523,524,1,0,0,0,524,525,6,13,0,0,525, + 42,1,0,0,0,526,527,7,2,0,0,527,528,7,5,0,0,528,529,7,12,0,0,529,530,7,5, + 0,0,530,531,7,2,0,0,531,532,1,0,0,0,532,533,6,14,0,0,533,44,1,0,0,0,534, + 535,7,19,0,0,535,536,7,10,0,0,536,537,7,3,0,0,537,538,7,6,0,0,538,539,7, + 3,0,0,539,540,1,0,0,0,540,541,6,15,0,0,541,46,1,0,0,0,542,543,4,16,0,0, + 543,544,7,1,0,0,544,545,7,9,0,0,545,546,7,13,0,0,546,547,7,1,0,0,547,548, + 7,9,0,0,548,549,7,3,0,0,549,550,7,2,0,0,550,551,7,5,0,0,551,552,7,12,0, + 0,552,553,7,5,0,0,553,554,7,2,0,0,554,555,1,0,0,0,555,556,6,16,0,0,556, + 48,1,0,0,0,557,558,4,17,1,0,558,559,7,13,0,0,559,560,7,7,0,0,560,561,7, + 7,0,0,561,562,7,18,0,0,562,563,7,20,0,0,563,564,7,8,0,0,564,565,1,0,0,0, + 565,566,6,17,8,0,566,50,1,0,0,0,567,568,4,18,2,0,568,569,7,16,0,0,569,570, + 7,3,0,0,570,571,7,5,0,0,571,572,7,6,0,0,572,573,7,1,0,0,573,574,7,4,0,0, + 574,575,7,2,0,0,575,576,1,0,0,0,576,577,6,18,9,0,577,52,1,0,0,0,578,580, + 8,21,0,0,579,578,1,0,0,0,580,581,1,0,0,0,581,579,1,0,0,0,581,582,1,0,0, + 0,582,583,1,0,0,0,583,584,6,19,0,0,584,54,1,0,0,0,585,586,5,47,0,0,586, + 587,5,47,0,0,587,591,1,0,0,0,588,590,8,22,0,0,589,588,1,0,0,0,590,593,1, + 0,0,0,591,589,1,0,0,0,591,592,1,0,0,0,592,595,1,0,0,0,593,591,1,0,0,0,594, + 596,5,13,0,0,595,594,1,0,0,0,595,596,1,0,0,0,596,598,1,0,0,0,597,599,5, + 10,0,0,598,597,1,0,0,0,598,599,1,0,0,0,599,600,1,0,0,0,600,601,6,20,10, + 0,601,56,1,0,0,0,602,603,5,47,0,0,603,604,5,42,0,0,604,609,1,0,0,0,605, + 608,3,57,21,0,606,608,9,0,0,0,607,605,1,0,0,0,607,606,1,0,0,0,608,611,1, + 0,0,0,609,610,1,0,0,0,609,607,1,0,0,0,610,612,1,0,0,0,611,609,1,0,0,0,612, + 613,5,42,0,0,613,614,5,47,0,0,614,615,1,0,0,0,615,616,6,21,10,0,616,58, + 1,0,0,0,617,619,7,23,0,0,618,617,1,0,0,0,619,620,1,0,0,0,620,618,1,0,0, + 0,620,621,1,0,0,0,621,622,1,0,0,0,622,623,6,22,10,0,623,60,1,0,0,0,624, + 625,5,58,0,0,625,62,1,0,0,0,626,627,5,124,0,0,627,628,1,0,0,0,628,629,6, + 24,11,0,629,64,1,0,0,0,630,631,7,24,0,0,631,66,1,0,0,0,632,633,7,25,0,0, + 633,68,1,0,0,0,634,635,5,92,0,0,635,636,7,26,0,0,636,70,1,0,0,0,637,638, + 8,27,0,0,638,72,1,0,0,0,639,641,7,3,0,0,640,642,7,28,0,0,641,640,1,0,0, + 0,641,642,1,0,0,0,642,644,1,0,0,0,643,645,3,65,25,0,644,643,1,0,0,0,645, + 646,1,0,0,0,646,644,1,0,0,0,646,647,1,0,0,0,647,74,1,0,0,0,648,649,5,64, + 0,0,649,76,1,0,0,0,650,651,5,96,0,0,651,78,1,0,0,0,652,656,8,29,0,0,653, + 654,5,96,0,0,654,656,5,96,0,0,655,652,1,0,0,0,655,653,1,0,0,0,656,80,1, + 0,0,0,657,658,5,95,0,0,658,82,1,0,0,0,659,663,3,67,26,0,660,663,3,65,25, + 0,661,663,3,81,33,0,662,659,1,0,0,0,662,660,1,0,0,0,662,661,1,0,0,0,663, + 84,1,0,0,0,664,669,5,34,0,0,665,668,3,69,27,0,666,668,3,71,28,0,667,665, + 1,0,0,0,667,666,1,0,0,0,668,671,1,0,0,0,669,667,1,0,0,0,669,670,1,0,0,0, + 670,672,1,0,0,0,671,669,1,0,0,0,672,694,5,34,0,0,673,674,5,34,0,0,674,675, + 5,34,0,0,675,676,5,34,0,0,676,680,1,0,0,0,677,679,8,22,0,0,678,677,1,0, + 0,0,679,682,1,0,0,0,680,681,1,0,0,0,680,678,1,0,0,0,681,683,1,0,0,0,682, + 680,1,0,0,0,683,684,5,34,0,0,684,685,5,34,0,0,685,686,5,34,0,0,686,688, + 1,0,0,0,687,689,5,34,0,0,688,687,1,0,0,0,688,689,1,0,0,0,689,691,1,0,0, + 0,690,692,5,34,0,0,691,690,1,0,0,0,691,692,1,0,0,0,692,694,1,0,0,0,693, + 664,1,0,0,0,693,673,1,0,0,0,694,86,1,0,0,0,695,697,3,65,25,0,696,695,1, + 0,0,0,697,698,1,0,0,0,698,696,1,0,0,0,698,699,1,0,0,0,699,88,1,0,0,0,700, + 702,3,65,25,0,701,700,1,0,0,0,702,703,1,0,0,0,703,701,1,0,0,0,703,704,1, + 0,0,0,704,705,1,0,0,0,705,709,3,105,45,0,706,708,3,65,25,0,707,706,1,0, + 0,0,708,711,1,0,0,0,709,707,1,0,0,0,709,710,1,0,0,0,710,743,1,0,0,0,711, + 709,1,0,0,0,712,714,3,105,45,0,713,715,3,65,25,0,714,713,1,0,0,0,715,716, + 1,0,0,0,716,714,1,0,0,0,716,717,1,0,0,0,717,743,1,0,0,0,718,720,3,65,25, + 0,719,718,1,0,0,0,720,721,1,0,0,0,721,719,1,0,0,0,721,722,1,0,0,0,722,730, + 1,0,0,0,723,727,3,105,45,0,724,726,3,65,25,0,725,724,1,0,0,0,726,729,1, + 0,0,0,727,725,1,0,0,0,727,728,1,0,0,0,728,731,1,0,0,0,729,727,1,0,0,0,730, + 723,1,0,0,0,730,731,1,0,0,0,731,732,1,0,0,0,732,733,3,73,29,0,733,743,1, + 0,0,0,734,736,3,105,45,0,735,737,3,65,25,0,736,735,1,0,0,0,737,738,1,0, + 0,0,738,736,1,0,0,0,738,739,1,0,0,0,739,740,1,0,0,0,740,741,3,73,29,0,741, + 743,1,0,0,0,742,701,1,0,0,0,742,712,1,0,0,0,742,719,1,0,0,0,742,734,1,0, + 0,0,743,90,1,0,0,0,744,745,7,30,0,0,745,746,7,31,0,0,746,92,1,0,0,0,747, + 748,7,12,0,0,748,749,7,9,0,0,749,750,7,0,0,0,750,94,1,0,0,0,751,752,7,12, + 0,0,752,753,7,2,0,0,753,754,7,4,0,0,754,96,1,0,0,0,755,756,5,61,0,0,756, + 98,1,0,0,0,757,758,5,58,0,0,758,759,5,58,0,0,759,100,1,0,0,0,760,761,5, + 44,0,0,761,102,1,0,0,0,762,763,7,0,0,0,763,764,7,3,0,0,764,765,7,2,0,0, + 765,766,7,4,0,0,766,104,1,0,0,0,767,768,5,46,0,0,768,106,1,0,0,0,769,770, + 7,15,0,0,770,771,7,12,0,0,771,772,7,13,0,0,772,773,7,2,0,0,773,774,7,3, + 0,0,774,108,1,0,0,0,775,776,7,15,0,0,776,777,7,1,0,0,777,778,7,6,0,0,778, + 779,7,2,0,0,779,780,7,5,0,0,780,110,1,0,0,0,781,782,7,1,0,0,782,783,7,9, + 0,0,783,112,1,0,0,0,784,785,7,1,0,0,785,786,7,2,0,0,786,114,1,0,0,0,787, + 788,7,13,0,0,788,789,7,12,0,0,789,790,7,2,0,0,790,791,7,5,0,0,791,116,1, + 0,0,0,792,793,7,13,0,0,793,794,7,1,0,0,794,795,7,18,0,0,795,796,7,3,0,0, + 796,118,1,0,0,0,797,798,5,40,0,0,798,120,1,0,0,0,799,800,7,9,0,0,800,801, + 7,7,0,0,801,802,7,5,0,0,802,122,1,0,0,0,803,804,7,9,0,0,804,805,7,20,0, + 0,805,806,7,13,0,0,806,807,7,13,0,0,807,124,1,0,0,0,808,809,7,9,0,0,809, + 810,7,20,0,0,810,811,7,13,0,0,811,812,7,13,0,0,812,813,7,2,0,0,813,126, + 1,0,0,0,814,815,7,7,0,0,815,816,7,6,0,0,816,128,1,0,0,0,817,818,5,63,0, + 0,818,130,1,0,0,0,819,820,7,6,0,0,820,821,7,13,0,0,821,822,7,1,0,0,822, + 823,7,18,0,0,823,824,7,3,0,0,824,132,1,0,0,0,825,826,5,41,0,0,826,134,1, + 0,0,0,827,828,7,5,0,0,828,829,7,6,0,0,829,830,7,20,0,0,830,831,7,3,0,0, + 831,136,1,0,0,0,832,833,5,61,0,0,833,834,5,61,0,0,834,138,1,0,0,0,835,836, + 5,61,0,0,836,837,5,126,0,0,837,140,1,0,0,0,838,839,5,33,0,0,839,840,5,61, + 0,0,840,142,1,0,0,0,841,842,5,60,0,0,842,144,1,0,0,0,843,844,5,60,0,0,844, + 845,5,61,0,0,845,146,1,0,0,0,846,847,5,62,0,0,847,148,1,0,0,0,848,849,5, + 62,0,0,849,850,5,61,0,0,850,150,1,0,0,0,851,852,5,43,0,0,852,152,1,0,0, + 0,853,854,5,45,0,0,854,154,1,0,0,0,855,856,5,42,0,0,856,156,1,0,0,0,857, + 858,5,47,0,0,858,158,1,0,0,0,859,860,5,37,0,0,860,160,1,0,0,0,861,862,4, + 73,3,0,862,863,3,61,23,0,863,864,1,0,0,0,864,865,6,73,12,0,865,162,1,0, + 0,0,866,867,3,45,15,0,867,868,1,0,0,0,868,869,6,74,13,0,869,164,1,0,0,0, + 870,873,3,129,57,0,871,874,3,67,26,0,872,874,3,81,33,0,873,871,1,0,0,0, + 873,872,1,0,0,0,874,878,1,0,0,0,875,877,3,83,34,0,876,875,1,0,0,0,877,880, + 1,0,0,0,878,876,1,0,0,0,878,879,1,0,0,0,879,888,1,0,0,0,880,878,1,0,0,0, + 881,883,3,129,57,0,882,884,3,65,25,0,883,882,1,0,0,0,884,885,1,0,0,0,885, + 883,1,0,0,0,885,886,1,0,0,0,886,888,1,0,0,0,887,870,1,0,0,0,887,881,1,0, + 0,0,888,166,1,0,0,0,889,890,5,91,0,0,890,891,1,0,0,0,891,892,6,76,0,0,892, + 893,6,76,0,0,893,168,1,0,0,0,894,895,5,93,0,0,895,896,1,0,0,0,896,897,6, + 77,11,0,897,898,6,77,11,0,898,170,1,0,0,0,899,903,3,67,26,0,900,902,3,83, + 34,0,901,900,1,0,0,0,902,905,1,0,0,0,903,901,1,0,0,0,903,904,1,0,0,0,904, + 916,1,0,0,0,905,903,1,0,0,0,906,909,3,81,33,0,907,909,3,75,30,0,908,906, + 1,0,0,0,908,907,1,0,0,0,909,911,1,0,0,0,910,912,3,83,34,0,911,910,1,0,0, + 0,912,913,1,0,0,0,913,911,1,0,0,0,913,914,1,0,0,0,914,916,1,0,0,0,915,899, + 1,0,0,0,915,908,1,0,0,0,916,172,1,0,0,0,917,919,3,77,31,0,918,920,3,79, + 32,0,919,918,1,0,0,0,920,921,1,0,0,0,921,919,1,0,0,0,921,922,1,0,0,0,922, + 923,1,0,0,0,923,924,3,77,31,0,924,174,1,0,0,0,925,926,3,173,79,0,926,176, + 1,0,0,0,927,928,3,55,20,0,928,929,1,0,0,0,929,930,6,81,10,0,930,178,1,0, + 0,0,931,932,3,57,21,0,932,933,1,0,0,0,933,934,6,82,10,0,934,180,1,0,0,0, + 935,936,3,59,22,0,936,937,1,0,0,0,937,938,6,83,10,0,938,182,1,0,0,0,939, + 940,3,167,76,0,940,941,1,0,0,0,941,942,6,84,14,0,942,943,6,84,15,0,943, + 184,1,0,0,0,944,945,3,63,24,0,945,946,1,0,0,0,946,947,6,85,16,0,947,948, + 6,85,11,0,948,186,1,0,0,0,949,950,3,59,22,0,950,951,1,0,0,0,951,952,6,86, + 10,0,952,188,1,0,0,0,953,954,3,55,20,0,954,955,1,0,0,0,955,956,6,87,10, + 0,956,190,1,0,0,0,957,958,3,57,21,0,958,959,1,0,0,0,959,960,6,88,10,0,960, + 192,1,0,0,0,961,962,3,63,24,0,962,963,1,0,0,0,963,964,6,89,16,0,964,965, + 6,89,11,0,965,194,1,0,0,0,966,967,3,167,76,0,967,968,1,0,0,0,968,969,6, + 90,14,0,969,196,1,0,0,0,970,971,3,169,77,0,971,972,1,0,0,0,972,973,6,91, + 17,0,973,198,1,0,0,0,974,975,3,61,23,0,975,976,1,0,0,0,976,977,6,92,12, + 0,977,200,1,0,0,0,978,979,3,101,43,0,979,980,1,0,0,0,980,981,6,93,18,0, + 981,202,1,0,0,0,982,983,3,97,41,0,983,984,1,0,0,0,984,985,6,94,19,0,985, + 204,1,0,0,0,986,987,7,16,0,0,987,988,7,3,0,0,988,989,7,5,0,0,989,990,7, + 12,0,0,990,991,7,0,0,0,991,992,7,12,0,0,992,993,7,5,0,0,993,994,7,12,0, + 0,994,206,1,0,0,0,995,999,8,32,0,0,996,997,5,47,0,0,997,999,8,33,0,0,998, + 995,1,0,0,0,998,996,1,0,0,0,999,208,1,0,0,0,1000,1002,3,207,96,0,1001,1000, + 1,0,0,0,1002,1003,1,0,0,0,1003,1001,1,0,0,0,1003,1004,1,0,0,0,1004,210, + 1,0,0,0,1005,1006,3,209,97,0,1006,1007,1,0,0,0,1007,1008,6,98,20,0,1008, + 212,1,0,0,0,1009,1010,3,85,35,0,1010,1011,1,0,0,0,1011,1012,6,99,21,0,1012, + 214,1,0,0,0,1013,1014,3,55,20,0,1014,1015,1,0,0,0,1015,1016,6,100,10,0, + 1016,216,1,0,0,0,1017,1018,3,57,21,0,1018,1019,1,0,0,0,1019,1020,6,101, + 10,0,1020,218,1,0,0,0,1021,1022,3,59,22,0,1022,1023,1,0,0,0,1023,1024,6, + 102,10,0,1024,220,1,0,0,0,1025,1026,3,63,24,0,1026,1027,1,0,0,0,1027,1028, + 6,103,16,0,1028,1029,6,103,11,0,1029,222,1,0,0,0,1030,1031,3,105,45,0,1031, + 1032,1,0,0,0,1032,1033,6,104,22,0,1033,224,1,0,0,0,1034,1035,3,101,43,0, + 1035,1036,1,0,0,0,1036,1037,6,105,18,0,1037,226,1,0,0,0,1038,1039,4,106, + 4,0,1039,1040,3,129,57,0,1040,1041,1,0,0,0,1041,1042,6,106,23,0,1042,228, + 1,0,0,0,1043,1044,4,107,5,0,1044,1045,3,165,75,0,1045,1046,1,0,0,0,1046, + 1047,6,107,24,0,1047,230,1,0,0,0,1048,1053,3,67,26,0,1049,1053,3,65,25, + 0,1050,1053,3,81,33,0,1051,1053,3,155,70,0,1052,1048,1,0,0,0,1052,1049, + 1,0,0,0,1052,1050,1,0,0,0,1052,1051,1,0,0,0,1053,232,1,0,0,0,1054,1057, + 3,67,26,0,1055,1057,3,155,70,0,1056,1054,1,0,0,0,1056,1055,1,0,0,0,1057, + 1061,1,0,0,0,1058,1060,3,231,108,0,1059,1058,1,0,0,0,1060,1063,1,0,0,0, + 1061,1059,1,0,0,0,1061,1062,1,0,0,0,1062,1074,1,0,0,0,1063,1061,1,0,0,0, + 1064,1067,3,81,33,0,1065,1067,3,75,30,0,1066,1064,1,0,0,0,1066,1065,1,0, + 0,0,1067,1069,1,0,0,0,1068,1070,3,231,108,0,1069,1068,1,0,0,0,1070,1071, + 1,0,0,0,1071,1069,1,0,0,0,1071,1072,1,0,0,0,1072,1074,1,0,0,0,1073,1056, + 1,0,0,0,1073,1066,1,0,0,0,1074,234,1,0,0,0,1075,1078,3,233,109,0,1076,1078, + 3,173,79,0,1077,1075,1,0,0,0,1077,1076,1,0,0,0,1078,1079,1,0,0,0,1079,1077, + 1,0,0,0,1079,1080,1,0,0,0,1080,236,1,0,0,0,1081,1082,3,55,20,0,1082,1083, + 1,0,0,0,1083,1084,6,111,10,0,1084,238,1,0,0,0,1085,1086,3,57,21,0,1086, + 1087,1,0,0,0,1087,1088,6,112,10,0,1088,240,1,0,0,0,1089,1090,3,59,22,0, + 1090,1091,1,0,0,0,1091,1092,6,113,10,0,1092,242,1,0,0,0,1093,1094,3,63, + 24,0,1094,1095,1,0,0,0,1095,1096,6,114,16,0,1096,1097,6,114,11,0,1097,244, + 1,0,0,0,1098,1099,3,97,41,0,1099,1100,1,0,0,0,1100,1101,6,115,19,0,1101, + 246,1,0,0,0,1102,1103,3,101,43,0,1103,1104,1,0,0,0,1104,1105,6,116,18,0, + 1105,248,1,0,0,0,1106,1107,3,105,45,0,1107,1108,1,0,0,0,1108,1109,6,117, + 22,0,1109,250,1,0,0,0,1110,1111,4,118,6,0,1111,1112,3,129,57,0,1112,1113, + 1,0,0,0,1113,1114,6,118,23,0,1114,252,1,0,0,0,1115,1116,4,119,7,0,1116, + 1117,3,165,75,0,1117,1118,1,0,0,0,1118,1119,6,119,24,0,1119,254,1,0,0,0, + 1120,1121,7,12,0,0,1121,1122,7,2,0,0,1122,256,1,0,0,0,1123,1124,3,235,110, + 0,1124,1125,1,0,0,0,1125,1126,6,121,25,0,1126,258,1,0,0,0,1127,1128,3,55, + 20,0,1128,1129,1,0,0,0,1129,1130,6,122,10,0,1130,260,1,0,0,0,1131,1132, + 3,57,21,0,1132,1133,1,0,0,0,1133,1134,6,123,10,0,1134,262,1,0,0,0,1135, + 1136,3,59,22,0,1136,1137,1,0,0,0,1137,1138,6,124,10,0,1138,264,1,0,0,0, + 1139,1140,3,63,24,0,1140,1141,1,0,0,0,1141,1142,6,125,16,0,1142,1143,6, + 125,11,0,1143,266,1,0,0,0,1144,1145,3,167,76,0,1145,1146,1,0,0,0,1146,1147, + 6,126,14,0,1147,1148,6,126,26,0,1148,268,1,0,0,0,1149,1150,7,7,0,0,1150, + 1151,7,9,0,0,1151,1152,1,0,0,0,1152,1153,6,127,27,0,1153,270,1,0,0,0,1154, + 1155,7,19,0,0,1155,1156,7,1,0,0,1156,1157,7,5,0,0,1157,1158,7,10,0,0,1158, + 1159,1,0,0,0,1159,1160,6,128,27,0,1160,272,1,0,0,0,1161,1162,8,34,0,0,1162, + 274,1,0,0,0,1163,1165,3,273,129,0,1164,1163,1,0,0,0,1165,1166,1,0,0,0,1166, + 1164,1,0,0,0,1166,1167,1,0,0,0,1167,1168,1,0,0,0,1168,1169,3,61,23,0,1169, + 1171,1,0,0,0,1170,1164,1,0,0,0,1170,1171,1,0,0,0,1171,1173,1,0,0,0,1172, + 1174,3,273,129,0,1173,1172,1,0,0,0,1174,1175,1,0,0,0,1175,1173,1,0,0,0, + 1175,1176,1,0,0,0,1176,276,1,0,0,0,1177,1178,3,275,130,0,1178,1179,1,0, + 0,0,1179,1180,6,131,28,0,1180,278,1,0,0,0,1181,1182,3,55,20,0,1182,1183, + 1,0,0,0,1183,1184,6,132,10,0,1184,280,1,0,0,0,1185,1186,3,57,21,0,1186, + 1187,1,0,0,0,1187,1188,6,133,10,0,1188,282,1,0,0,0,1189,1190,3,59,22,0, + 1190,1191,1,0,0,0,1191,1192,6,134,10,0,1192,284,1,0,0,0,1193,1194,3,63, + 24,0,1194,1195,1,0,0,0,1195,1196,6,135,16,0,1196,1197,6,135,11,0,1197,1198, + 6,135,11,0,1198,286,1,0,0,0,1199,1200,3,97,41,0,1200,1201,1,0,0,0,1201, + 1202,6,136,19,0,1202,288,1,0,0,0,1203,1204,3,101,43,0,1204,1205,1,0,0,0, + 1205,1206,6,137,18,0,1206,290,1,0,0,0,1207,1208,3,105,45,0,1208,1209,1, + 0,0,0,1209,1210,6,138,22,0,1210,292,1,0,0,0,1211,1212,3,271,128,0,1212, + 1213,1,0,0,0,1213,1214,6,139,29,0,1214,294,1,0,0,0,1215,1216,3,235,110, + 0,1216,1217,1,0,0,0,1217,1218,6,140,25,0,1218,296,1,0,0,0,1219,1220,3,175, + 80,0,1220,1221,1,0,0,0,1221,1222,6,141,30,0,1222,298,1,0,0,0,1223,1224, + 4,142,8,0,1224,1225,3,129,57,0,1225,1226,1,0,0,0,1226,1227,6,142,23,0,1227, + 300,1,0,0,0,1228,1229,4,143,9,0,1229,1230,3,165,75,0,1230,1231,1,0,0,0, + 1231,1232,6,143,24,0,1232,302,1,0,0,0,1233,1234,3,55,20,0,1234,1235,1,0, + 0,0,1235,1236,6,144,10,0,1236,304,1,0,0,0,1237,1238,3,57,21,0,1238,1239, + 1,0,0,0,1239,1240,6,145,10,0,1240,306,1,0,0,0,1241,1242,3,59,22,0,1242, + 1243,1,0,0,0,1243,1244,6,146,10,0,1244,308,1,0,0,0,1245,1246,3,63,24,0, + 1246,1247,1,0,0,0,1247,1248,6,147,16,0,1248,1249,6,147,11,0,1249,310,1, + 0,0,0,1250,1251,3,105,45,0,1251,1252,1,0,0,0,1252,1253,6,148,22,0,1253, + 312,1,0,0,0,1254,1255,4,149,10,0,1255,1256,3,129,57,0,1256,1257,1,0,0,0, + 1257,1258,6,149,23,0,1258,314,1,0,0,0,1259,1260,4,150,11,0,1260,1261,3, + 165,75,0,1261,1262,1,0,0,0,1262,1263,6,150,24,0,1263,316,1,0,0,0,1264,1265, + 3,175,80,0,1265,1266,1,0,0,0,1266,1267,6,151,30,0,1267,318,1,0,0,0,1268, + 1269,3,171,78,0,1269,1270,1,0,0,0,1270,1271,6,152,31,0,1271,320,1,0,0,0, + 1272,1273,3,55,20,0,1273,1274,1,0,0,0,1274,1275,6,153,10,0,1275,322,1,0, + 0,0,1276,1277,3,57,21,0,1277,1278,1,0,0,0,1278,1279,6,154,10,0,1279,324, + 1,0,0,0,1280,1281,3,59,22,0,1281,1282,1,0,0,0,1282,1283,6,155,10,0,1283, + 326,1,0,0,0,1284,1285,3,63,24,0,1285,1286,1,0,0,0,1286,1287,6,156,16,0, + 1287,1288,6,156,11,0,1288,328,1,0,0,0,1289,1290,7,1,0,0,1290,1291,7,9,0, + 0,1291,1292,7,15,0,0,1292,1293,7,7,0,0,1293,330,1,0,0,0,1294,1295,3,55, + 20,0,1295,1296,1,0,0,0,1296,1297,6,158,10,0,1297,332,1,0,0,0,1298,1299, + 3,57,21,0,1299,1300,1,0,0,0,1300,1301,6,159,10,0,1301,334,1,0,0,0,1302, + 1303,3,59,22,0,1303,1304,1,0,0,0,1304,1305,6,160,10,0,1305,336,1,0,0,0, + 1306,1307,3,169,77,0,1307,1308,1,0,0,0,1308,1309,6,161,17,0,1309,1310,6, + 161,11,0,1310,338,1,0,0,0,1311,1312,3,61,23,0,1312,1313,1,0,0,0,1313,1314, + 6,162,12,0,1314,340,1,0,0,0,1315,1321,3,75,30,0,1316,1321,3,65,25,0,1317, + 1321,3,105,45,0,1318,1321,3,67,26,0,1319,1321,3,81,33,0,1320,1315,1,0,0, + 0,1320,1316,1,0,0,0,1320,1317,1,0,0,0,1320,1318,1,0,0,0,1320,1319,1,0,0, + 0,1321,1322,1,0,0,0,1322,1320,1,0,0,0,1322,1323,1,0,0,0,1323,342,1,0,0, + 0,1324,1325,3,55,20,0,1325,1326,1,0,0,0,1326,1327,6,164,10,0,1327,344,1, + 0,0,0,1328,1329,3,57,21,0,1329,1330,1,0,0,0,1330,1331,6,165,10,0,1331,346, + 1,0,0,0,1332,1333,3,59,22,0,1333,1334,1,0,0,0,1334,1335,6,166,10,0,1335, + 348,1,0,0,0,1336,1337,3,63,24,0,1337,1338,1,0,0,0,1338,1339,6,167,16,0, + 1339,1340,6,167,11,0,1340,350,1,0,0,0,1341,1342,3,61,23,0,1342,1343,1,0, + 0,0,1343,1344,6,168,12,0,1344,352,1,0,0,0,1345,1346,3,101,43,0,1346,1347, + 1,0,0,0,1347,1348,6,169,18,0,1348,354,1,0,0,0,1349,1350,3,105,45,0,1350, + 1351,1,0,0,0,1351,1352,6,170,22,0,1352,356,1,0,0,0,1353,1354,3,269,127, + 0,1354,1355,1,0,0,0,1355,1356,6,171,32,0,1356,1357,6,171,33,0,1357,358, + 1,0,0,0,1358,1359,3,209,97,0,1359,1360,1,0,0,0,1360,1361,6,172,20,0,1361, + 360,1,0,0,0,1362,1363,3,85,35,0,1363,1364,1,0,0,0,1364,1365,6,173,21,0, + 1365,362,1,0,0,0,1366,1367,3,55,20,0,1367,1368,1,0,0,0,1368,1369,6,174, + 10,0,1369,364,1,0,0,0,1370,1371,3,57,21,0,1371,1372,1,0,0,0,1372,1373,6, + 175,10,0,1373,366,1,0,0,0,1374,1375,3,59,22,0,1375,1376,1,0,0,0,1376,1377, + 6,176,10,0,1377,368,1,0,0,0,1378,1379,3,63,24,0,1379,1380,1,0,0,0,1380, + 1381,6,177,16,0,1381,1382,6,177,11,0,1382,1383,6,177,11,0,1383,370,1,0, + 0,0,1384,1385,3,101,43,0,1385,1386,1,0,0,0,1386,1387,6,178,18,0,1387,372, + 1,0,0,0,1388,1389,3,105,45,0,1389,1390,1,0,0,0,1390,1391,6,179,22,0,1391, + 374,1,0,0,0,1392,1393,3,235,110,0,1393,1394,1,0,0,0,1394,1395,6,180,25, + 0,1395,376,1,0,0,0,1396,1397,3,55,20,0,1397,1398,1,0,0,0,1398,1399,6,181, + 10,0,1399,378,1,0,0,0,1400,1401,3,57,21,0,1401,1402,1,0,0,0,1402,1403,6, + 182,10,0,1403,380,1,0,0,0,1404,1405,3,59,22,0,1405,1406,1,0,0,0,1406,1407, + 6,183,10,0,1407,382,1,0,0,0,1408,1409,3,63,24,0,1409,1410,1,0,0,0,1410, + 1411,6,184,16,0,1411,1412,6,184,11,0,1412,384,1,0,0,0,1413,1414,3,209,97, + 0,1414,1415,1,0,0,0,1415,1416,6,185,20,0,1416,1417,6,185,11,0,1417,1418, + 6,185,34,0,1418,386,1,0,0,0,1419,1420,3,85,35,0,1420,1421,1,0,0,0,1421, + 1422,6,186,21,0,1422,1423,6,186,11,0,1423,1424,6,186,34,0,1424,388,1,0, + 0,0,1425,1426,3,55,20,0,1426,1427,1,0,0,0,1427,1428,6,187,10,0,1428,390, + 1,0,0,0,1429,1430,3,57,21,0,1430,1431,1,0,0,0,1431,1432,6,188,10,0,1432, + 392,1,0,0,0,1433,1434,3,59,22,0,1434,1435,1,0,0,0,1435,1436,6,189,10,0, + 1436,394,1,0,0,0,1437,1438,3,61,23,0,1438,1439,1,0,0,0,1439,1440,6,190, + 12,0,1440,1441,6,190,11,0,1441,1442,6,190,9,0,1442,396,1,0,0,0,1443,1444, + 3,101,43,0,1444,1445,1,0,0,0,1445,1446,6,191,18,0,1446,1447,6,191,11,0, + 1447,1448,6,191,9,0,1448,398,1,0,0,0,1449,1450,3,55,20,0,1450,1451,1,0, + 0,0,1451,1452,6,192,10,0,1452,400,1,0,0,0,1453,1454,3,57,21,0,1454,1455, + 1,0,0,0,1455,1456,6,193,10,0,1456,402,1,0,0,0,1457,1458,3,59,22,0,1458, + 1459,1,0,0,0,1459,1460,6,194,10,0,1460,404,1,0,0,0,1461,1462,3,175,80,0, + 1462,1463,1,0,0,0,1463,1464,6,195,11,0,1464,1465,6,195,0,0,1465,1466,6, + 195,30,0,1466,406,1,0,0,0,1467,1468,3,171,78,0,1468,1469,1,0,0,0,1469,1470, + 6,196,11,0,1470,1471,6,196,0,0,1471,1472,6,196,31,0,1472,408,1,0,0,0,1473, + 1474,3,91,38,0,1474,1475,1,0,0,0,1475,1476,6,197,11,0,1476,1477,6,197,0, + 0,1477,1478,6,197,35,0,1478,410,1,0,0,0,1479,1480,3,63,24,0,1480,1481,1, + 0,0,0,1481,1482,6,198,16,0,1482,1483,6,198,11,0,1483,412,1,0,0,0,65,0,1, + 2,3,4,5,6,7,8,9,10,11,12,13,14,581,591,595,598,607,609,620,641,646,655, + 662,667,669,680,688,691,693,698,703,709,716,721,727,730,738,742,873,878, + 885,887,903,908,913,915,921,998,1003,1052,1056,1061,1066,1071,1073,1077, + 1079,1166,1170,1175,1320,1322,36,5,1,0,5,4,0,5,6,0,5,2,0,5,3,0,5,8,0,5, + 5,0,5,9,0,5,11,0,5,13,0,0,1,0,4,0,0,7,24,0,7,16,0,7,65,0,5,0,0,7,25,0,7, + 66,0,7,34,0,7,32,0,7,76,0,7,26,0,7,36,0,7,48,0,7,64,0,7,80,0,5,10,0,5,7, + 0,7,90,0,7,89,0,7,68,0,7,67,0,7,88,0,5,12,0,5,14,0,7,29,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 index 261b4f712b5b..6a76e32d28f3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 @@ -79,7 +79,7 @@ regexBooleanExpression ; matchBooleanExpression - : valueExpression MATCH queryString=string + : fieldExp=qualifiedName COLON queryString=constant ; valueExpression @@ -107,9 +107,7 @@ functionExpression ; functionName - // Additional function identifiers that are already a reserved word in the language - : MATCH - | identifierOrParameter + : identifierOrParameter ; dataType diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.interp b/packages/kbn-esql-ast/src/antlr/esql_parser.interp index b52d842e79fb..a2b339f378f1 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.interp @@ -23,6 +23,7 @@ null null null null +':' '|' null null @@ -62,7 +63,6 @@ null '*' '/' '%' -'match' null null ']' @@ -103,7 +103,6 @@ null null null null -':' null null null @@ -146,6 +145,7 @@ UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS +COLON PIPE QUOTED_STRING INTEGER_LITERAL @@ -185,7 +185,6 @@ MINUS ASTERISK SLASH PERCENT -MATCH NAMED_OR_POSITIONAL_PARAM OPENING_BRACKET CLOSING_BRACKET @@ -226,7 +225,6 @@ INFO SHOW_LINE_COMMENT SHOW_MULTILINE_COMMENT SHOW_WS -COLON SETTING SETTING_LINE_COMMENT SETTTING_MULTILINE_COMMENT @@ -310,4 +308,4 @@ inlinestatsCommand atn: -[4, 1, 120, 605, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 134, 8, 1, 10, 1, 12, 1, 137, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 145, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 163, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 175, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 182, 8, 5, 10, 5, 12, 5, 185, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 192, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 198, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 206, 8, 5, 10, 5, 12, 5, 209, 9, 5, 1, 6, 1, 6, 3, 6, 213, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 220, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 225, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 236, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 242, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 250, 8, 9, 10, 9, 12, 9, 253, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 263, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 268, 8, 10, 10, 10, 12, 10, 271, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 279, 8, 11, 10, 11, 12, 11, 282, 9, 11, 3, 11, 284, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 3, 12, 290, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 5, 15, 300, 8, 15, 10, 15, 12, 15, 303, 9, 15, 1, 16, 1, 16, 1, 16, 3, 16, 308, 8, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 316, 8, 17, 10, 17, 12, 17, 319, 9, 17, 1, 17, 3, 17, 322, 8, 17, 1, 18, 1, 18, 1, 18, 3, 18, 327, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 337, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 343, 8, 22, 10, 22, 12, 22, 346, 9, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 356, 8, 24, 10, 24, 12, 24, 359, 9, 24, 1, 24, 3, 24, 362, 8, 24, 1, 24, 1, 24, 3, 24, 366, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 373, 8, 26, 1, 26, 1, 26, 3, 26, 377, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 382, 8, 27, 10, 27, 12, 27, 385, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 390, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 395, 8, 29, 10, 29, 12, 29, 398, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 403, 8, 30, 10, 30, 12, 30, 406, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 411, 8, 31, 10, 31, 12, 31, 414, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 421, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 436, 8, 34, 10, 34, 12, 34, 439, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 447, 8, 34, 10, 34, 12, 34, 450, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 458, 8, 34, 10, 34, 12, 34, 461, 9, 34, 1, 34, 1, 34, 3, 34, 465, 8, 34, 1, 35, 1, 35, 3, 35, 469, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 474, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 483, 8, 38, 10, 38, 12, 38, 486, 9, 38, 1, 39, 1, 39, 3, 39, 490, 8, 39, 1, 39, 1, 39, 3, 39, 494, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 506, 8, 42, 10, 42, 12, 42, 509, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 519, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 531, 8, 47, 10, 47, 12, 47, 534, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 544, 8, 50, 1, 51, 3, 51, 547, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 552, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 574, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 580, 8, 58, 10, 58, 12, 58, 583, 9, 58, 3, 58, 585, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 590, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 603, 8, 61, 1, 61, 0, 4, 2, 10, 18, 20, 62, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 0, 8, 1, 0, 58, 59, 1, 0, 60, 62, 2, 0, 25, 25, 76, 76, 1, 0, 67, 68, 2, 0, 30, 30, 34, 34, 2, 0, 37, 37, 40, 40, 2, 0, 36, 36, 50, 50, 2, 0, 51, 51, 53, 57, 631, 0, 124, 1, 0, 0, 0, 2, 127, 1, 0, 0, 0, 4, 144, 1, 0, 0, 0, 6, 162, 1, 0, 0, 0, 8, 164, 1, 0, 0, 0, 10, 197, 1, 0, 0, 0, 12, 224, 1, 0, 0, 0, 14, 226, 1, 0, 0, 0, 16, 235, 1, 0, 0, 0, 18, 241, 1, 0, 0, 0, 20, 262, 1, 0, 0, 0, 22, 272, 1, 0, 0, 0, 24, 289, 1, 0, 0, 0, 26, 291, 1, 0, 0, 0, 28, 293, 1, 0, 0, 0, 30, 296, 1, 0, 0, 0, 32, 307, 1, 0, 0, 0, 34, 311, 1, 0, 0, 0, 36, 326, 1, 0, 0, 0, 38, 330, 1, 0, 0, 0, 40, 332, 1, 0, 0, 0, 42, 336, 1, 0, 0, 0, 44, 338, 1, 0, 0, 0, 46, 347, 1, 0, 0, 0, 48, 351, 1, 0, 0, 0, 50, 367, 1, 0, 0, 0, 52, 370, 1, 0, 0, 0, 54, 378, 1, 0, 0, 0, 56, 386, 1, 0, 0, 0, 58, 391, 1, 0, 0, 0, 60, 399, 1, 0, 0, 0, 62, 407, 1, 0, 0, 0, 64, 415, 1, 0, 0, 0, 66, 420, 1, 0, 0, 0, 68, 464, 1, 0, 0, 0, 70, 468, 1, 0, 0, 0, 72, 473, 1, 0, 0, 0, 74, 475, 1, 0, 0, 0, 76, 478, 1, 0, 0, 0, 78, 487, 1, 0, 0, 0, 80, 495, 1, 0, 0, 0, 82, 498, 1, 0, 0, 0, 84, 501, 1, 0, 0, 0, 86, 510, 1, 0, 0, 0, 88, 514, 1, 0, 0, 0, 90, 520, 1, 0, 0, 0, 92, 524, 1, 0, 0, 0, 94, 527, 1, 0, 0, 0, 96, 535, 1, 0, 0, 0, 98, 539, 1, 0, 0, 0, 100, 543, 1, 0, 0, 0, 102, 546, 1, 0, 0, 0, 104, 551, 1, 0, 0, 0, 106, 555, 1, 0, 0, 0, 108, 557, 1, 0, 0, 0, 110, 559, 1, 0, 0, 0, 112, 562, 1, 0, 0, 0, 114, 566, 1, 0, 0, 0, 116, 569, 1, 0, 0, 0, 118, 589, 1, 0, 0, 0, 120, 593, 1, 0, 0, 0, 122, 598, 1, 0, 0, 0, 124, 125, 3, 2, 1, 0, 125, 126, 5, 0, 0, 1, 126, 1, 1, 0, 0, 0, 127, 128, 6, 1, -1, 0, 128, 129, 3, 4, 2, 0, 129, 135, 1, 0, 0, 0, 130, 131, 10, 1, 0, 0, 131, 132, 5, 24, 0, 0, 132, 134, 3, 6, 3, 0, 133, 130, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 3, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 138, 145, 3, 110, 55, 0, 139, 145, 3, 34, 17, 0, 140, 145, 3, 28, 14, 0, 141, 145, 3, 114, 57, 0, 142, 143, 4, 2, 1, 0, 143, 145, 3, 48, 24, 0, 144, 138, 1, 0, 0, 0, 144, 139, 1, 0, 0, 0, 144, 140, 1, 0, 0, 0, 144, 141, 1, 0, 0, 0, 144, 142, 1, 0, 0, 0, 145, 5, 1, 0, 0, 0, 146, 163, 3, 50, 25, 0, 147, 163, 3, 8, 4, 0, 148, 163, 3, 80, 40, 0, 149, 163, 3, 74, 37, 0, 150, 163, 3, 52, 26, 0, 151, 163, 3, 76, 38, 0, 152, 163, 3, 82, 41, 0, 153, 163, 3, 84, 42, 0, 154, 163, 3, 88, 44, 0, 155, 163, 3, 90, 45, 0, 156, 163, 3, 116, 58, 0, 157, 163, 3, 92, 46, 0, 158, 159, 4, 3, 2, 0, 159, 163, 3, 122, 61, 0, 160, 161, 4, 3, 3, 0, 161, 163, 3, 120, 60, 0, 162, 146, 1, 0, 0, 0, 162, 147, 1, 0, 0, 0, 162, 148, 1, 0, 0, 0, 162, 149, 1, 0, 0, 0, 162, 150, 1, 0, 0, 0, 162, 151, 1, 0, 0, 0, 162, 152, 1, 0, 0, 0, 162, 153, 1, 0, 0, 0, 162, 154, 1, 0, 0, 0, 162, 155, 1, 0, 0, 0, 162, 156, 1, 0, 0, 0, 162, 157, 1, 0, 0, 0, 162, 158, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 163, 7, 1, 0, 0, 0, 164, 165, 5, 16, 0, 0, 165, 166, 3, 10, 5, 0, 166, 9, 1, 0, 0, 0, 167, 168, 6, 5, -1, 0, 168, 169, 5, 43, 0, 0, 169, 198, 3, 10, 5, 8, 170, 198, 3, 16, 8, 0, 171, 198, 3, 12, 6, 0, 172, 174, 3, 16, 8, 0, 173, 175, 5, 43, 0, 0, 174, 173, 1, 0, 0, 0, 174, 175, 1, 0, 0, 0, 175, 176, 1, 0, 0, 0, 176, 177, 5, 38, 0, 0, 177, 178, 5, 42, 0, 0, 178, 183, 3, 16, 8, 0, 179, 180, 5, 33, 0, 0, 180, 182, 3, 16, 8, 0, 181, 179, 1, 0, 0, 0, 182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 186, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 186, 187, 5, 49, 0, 0, 187, 198, 1, 0, 0, 0, 188, 189, 3, 16, 8, 0, 189, 191, 5, 39, 0, 0, 190, 192, 5, 43, 0, 0, 191, 190, 1, 0, 0, 0, 191, 192, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 194, 5, 44, 0, 0, 194, 198, 1, 0, 0, 0, 195, 196, 4, 5, 4, 0, 196, 198, 3, 14, 7, 0, 197, 167, 1, 0, 0, 0, 197, 170, 1, 0, 0, 0, 197, 171, 1, 0, 0, 0, 197, 172, 1, 0, 0, 0, 197, 188, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 207, 1, 0, 0, 0, 199, 200, 10, 5, 0, 0, 200, 201, 5, 29, 0, 0, 201, 206, 3, 10, 5, 6, 202, 203, 10, 4, 0, 0, 203, 204, 5, 46, 0, 0, 204, 206, 3, 10, 5, 5, 205, 199, 1, 0, 0, 0, 205, 202, 1, 0, 0, 0, 206, 209, 1, 0, 0, 0, 207, 205, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 11, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 210, 212, 3, 16, 8, 0, 211, 213, 5, 43, 0, 0, 212, 211, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 215, 5, 41, 0, 0, 215, 216, 3, 106, 53, 0, 216, 225, 1, 0, 0, 0, 217, 219, 3, 16, 8, 0, 218, 220, 5, 43, 0, 0, 219, 218, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 222, 5, 48, 0, 0, 222, 223, 3, 106, 53, 0, 223, 225, 1, 0, 0, 0, 224, 210, 1, 0, 0, 0, 224, 217, 1, 0, 0, 0, 225, 13, 1, 0, 0, 0, 226, 227, 3, 16, 8, 0, 227, 228, 5, 63, 0, 0, 228, 229, 3, 106, 53, 0, 229, 15, 1, 0, 0, 0, 230, 236, 3, 18, 9, 0, 231, 232, 3, 18, 9, 0, 232, 233, 3, 108, 54, 0, 233, 234, 3, 18, 9, 0, 234, 236, 1, 0, 0, 0, 235, 230, 1, 0, 0, 0, 235, 231, 1, 0, 0, 0, 236, 17, 1, 0, 0, 0, 237, 238, 6, 9, -1, 0, 238, 242, 3, 20, 10, 0, 239, 240, 7, 0, 0, 0, 240, 242, 3, 18, 9, 3, 241, 237, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 251, 1, 0, 0, 0, 243, 244, 10, 2, 0, 0, 244, 245, 7, 1, 0, 0, 245, 250, 3, 18, 9, 3, 246, 247, 10, 1, 0, 0, 247, 248, 7, 0, 0, 0, 248, 250, 3, 18, 9, 2, 249, 243, 1, 0, 0, 0, 249, 246, 1, 0, 0, 0, 250, 253, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 19, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 254, 255, 6, 10, -1, 0, 255, 263, 3, 68, 34, 0, 256, 263, 3, 58, 29, 0, 257, 263, 3, 22, 11, 0, 258, 259, 5, 42, 0, 0, 259, 260, 3, 10, 5, 0, 260, 261, 5, 49, 0, 0, 261, 263, 1, 0, 0, 0, 262, 254, 1, 0, 0, 0, 262, 256, 1, 0, 0, 0, 262, 257, 1, 0, 0, 0, 262, 258, 1, 0, 0, 0, 263, 269, 1, 0, 0, 0, 264, 265, 10, 1, 0, 0, 265, 266, 5, 32, 0, 0, 266, 268, 3, 26, 13, 0, 267, 264, 1, 0, 0, 0, 268, 271, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 21, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 272, 273, 3, 24, 12, 0, 273, 283, 5, 42, 0, 0, 274, 284, 5, 60, 0, 0, 275, 280, 3, 10, 5, 0, 276, 277, 5, 33, 0, 0, 277, 279, 3, 10, 5, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 274, 1, 0, 0, 0, 283, 275, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 286, 5, 49, 0, 0, 286, 23, 1, 0, 0, 0, 287, 290, 5, 63, 0, 0, 288, 290, 3, 72, 36, 0, 289, 287, 1, 0, 0, 0, 289, 288, 1, 0, 0, 0, 290, 25, 1, 0, 0, 0, 291, 292, 3, 64, 32, 0, 292, 27, 1, 0, 0, 0, 293, 294, 5, 12, 0, 0, 294, 295, 3, 30, 15, 0, 295, 29, 1, 0, 0, 0, 296, 301, 3, 32, 16, 0, 297, 298, 5, 33, 0, 0, 298, 300, 3, 32, 16, 0, 299, 297, 1, 0, 0, 0, 300, 303, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 31, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 304, 305, 3, 58, 29, 0, 305, 306, 5, 31, 0, 0, 306, 308, 1, 0, 0, 0, 307, 304, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 1, 0, 0, 0, 309, 310, 3, 10, 5, 0, 310, 33, 1, 0, 0, 0, 311, 312, 5, 6, 0, 0, 312, 317, 3, 36, 18, 0, 313, 314, 5, 33, 0, 0, 314, 316, 3, 36, 18, 0, 315, 313, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 321, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 3, 42, 21, 0, 321, 320, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 35, 1, 0, 0, 0, 323, 324, 3, 38, 19, 0, 324, 325, 5, 104, 0, 0, 325, 327, 1, 0, 0, 0, 326, 323, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328, 329, 3, 40, 20, 0, 329, 37, 1, 0, 0, 0, 330, 331, 5, 76, 0, 0, 331, 39, 1, 0, 0, 0, 332, 333, 7, 2, 0, 0, 333, 41, 1, 0, 0, 0, 334, 337, 3, 44, 22, 0, 335, 337, 3, 46, 23, 0, 336, 334, 1, 0, 0, 0, 336, 335, 1, 0, 0, 0, 337, 43, 1, 0, 0, 0, 338, 339, 5, 75, 0, 0, 339, 344, 5, 76, 0, 0, 340, 341, 5, 33, 0, 0, 341, 343, 5, 76, 0, 0, 342, 340, 1, 0, 0, 0, 343, 346, 1, 0, 0, 0, 344, 342, 1, 0, 0, 0, 344, 345, 1, 0, 0, 0, 345, 45, 1, 0, 0, 0, 346, 344, 1, 0, 0, 0, 347, 348, 5, 65, 0, 0, 348, 349, 3, 44, 22, 0, 349, 350, 5, 66, 0, 0, 350, 47, 1, 0, 0, 0, 351, 352, 5, 19, 0, 0, 352, 357, 3, 36, 18, 0, 353, 354, 5, 33, 0, 0, 354, 356, 3, 36, 18, 0, 355, 353, 1, 0, 0, 0, 356, 359, 1, 0, 0, 0, 357, 355, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 361, 1, 0, 0, 0, 359, 357, 1, 0, 0, 0, 360, 362, 3, 54, 27, 0, 361, 360, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 365, 1, 0, 0, 0, 363, 364, 5, 28, 0, 0, 364, 366, 3, 30, 15, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 49, 1, 0, 0, 0, 367, 368, 5, 4, 0, 0, 368, 369, 3, 30, 15, 0, 369, 51, 1, 0, 0, 0, 370, 372, 5, 15, 0, 0, 371, 373, 3, 54, 27, 0, 372, 371, 1, 0, 0, 0, 372, 373, 1, 0, 0, 0, 373, 376, 1, 0, 0, 0, 374, 375, 5, 28, 0, 0, 375, 377, 3, 30, 15, 0, 376, 374, 1, 0, 0, 0, 376, 377, 1, 0, 0, 0, 377, 53, 1, 0, 0, 0, 378, 383, 3, 56, 28, 0, 379, 380, 5, 33, 0, 0, 380, 382, 3, 56, 28, 0, 381, 379, 1, 0, 0, 0, 382, 385, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 55, 1, 0, 0, 0, 385, 383, 1, 0, 0, 0, 386, 389, 3, 32, 16, 0, 387, 388, 5, 16, 0, 0, 388, 390, 3, 10, 5, 0, 389, 387, 1, 0, 0, 0, 389, 390, 1, 0, 0, 0, 390, 57, 1, 0, 0, 0, 391, 396, 3, 72, 36, 0, 392, 393, 5, 35, 0, 0, 393, 395, 3, 72, 36, 0, 394, 392, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 59, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 404, 3, 66, 33, 0, 400, 401, 5, 35, 0, 0, 401, 403, 3, 66, 33, 0, 402, 400, 1, 0, 0, 0, 403, 406, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 404, 405, 1, 0, 0, 0, 405, 61, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 407, 412, 3, 60, 30, 0, 408, 409, 5, 33, 0, 0, 409, 411, 3, 60, 30, 0, 410, 408, 1, 0, 0, 0, 411, 414, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 63, 1, 0, 0, 0, 414, 412, 1, 0, 0, 0, 415, 416, 7, 3, 0, 0, 416, 65, 1, 0, 0, 0, 417, 421, 5, 80, 0, 0, 418, 419, 4, 33, 10, 0, 419, 421, 3, 70, 35, 0, 420, 417, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 421, 67, 1, 0, 0, 0, 422, 465, 5, 44, 0, 0, 423, 424, 3, 104, 52, 0, 424, 425, 5, 67, 0, 0, 425, 465, 1, 0, 0, 0, 426, 465, 3, 102, 51, 0, 427, 465, 3, 104, 52, 0, 428, 465, 3, 98, 49, 0, 429, 465, 3, 70, 35, 0, 430, 465, 3, 106, 53, 0, 431, 432, 5, 65, 0, 0, 432, 437, 3, 100, 50, 0, 433, 434, 5, 33, 0, 0, 434, 436, 3, 100, 50, 0, 435, 433, 1, 0, 0, 0, 436, 439, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 437, 438, 1, 0, 0, 0, 438, 440, 1, 0, 0, 0, 439, 437, 1, 0, 0, 0, 440, 441, 5, 66, 0, 0, 441, 465, 1, 0, 0, 0, 442, 443, 5, 65, 0, 0, 443, 448, 3, 98, 49, 0, 444, 445, 5, 33, 0, 0, 445, 447, 3, 98, 49, 0, 446, 444, 1, 0, 0, 0, 447, 450, 1, 0, 0, 0, 448, 446, 1, 0, 0, 0, 448, 449, 1, 0, 0, 0, 449, 451, 1, 0, 0, 0, 450, 448, 1, 0, 0, 0, 451, 452, 5, 66, 0, 0, 452, 465, 1, 0, 0, 0, 453, 454, 5, 65, 0, 0, 454, 459, 3, 106, 53, 0, 455, 456, 5, 33, 0, 0, 456, 458, 3, 106, 53, 0, 457, 455, 1, 0, 0, 0, 458, 461, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, 1, 0, 0, 0, 461, 459, 1, 0, 0, 0, 462, 463, 5, 66, 0, 0, 463, 465, 1, 0, 0, 0, 464, 422, 1, 0, 0, 0, 464, 423, 1, 0, 0, 0, 464, 426, 1, 0, 0, 0, 464, 427, 1, 0, 0, 0, 464, 428, 1, 0, 0, 0, 464, 429, 1, 0, 0, 0, 464, 430, 1, 0, 0, 0, 464, 431, 1, 0, 0, 0, 464, 442, 1, 0, 0, 0, 464, 453, 1, 0, 0, 0, 465, 69, 1, 0, 0, 0, 466, 469, 5, 47, 0, 0, 467, 469, 5, 64, 0, 0, 468, 466, 1, 0, 0, 0, 468, 467, 1, 0, 0, 0, 469, 71, 1, 0, 0, 0, 470, 474, 3, 64, 32, 0, 471, 472, 4, 36, 11, 0, 472, 474, 3, 70, 35, 0, 473, 470, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 73, 1, 0, 0, 0, 475, 476, 5, 9, 0, 0, 476, 477, 5, 26, 0, 0, 477, 75, 1, 0, 0, 0, 478, 479, 5, 14, 0, 0, 479, 484, 3, 78, 39, 0, 480, 481, 5, 33, 0, 0, 481, 483, 3, 78, 39, 0, 482, 480, 1, 0, 0, 0, 483, 486, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 77, 1, 0, 0, 0, 486, 484, 1, 0, 0, 0, 487, 489, 3, 10, 5, 0, 488, 490, 7, 4, 0, 0, 489, 488, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 490, 493, 1, 0, 0, 0, 491, 492, 5, 45, 0, 0, 492, 494, 7, 5, 0, 0, 493, 491, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 79, 1, 0, 0, 0, 495, 496, 5, 8, 0, 0, 496, 497, 3, 62, 31, 0, 497, 81, 1, 0, 0, 0, 498, 499, 5, 2, 0, 0, 499, 500, 3, 62, 31, 0, 500, 83, 1, 0, 0, 0, 501, 502, 5, 11, 0, 0, 502, 507, 3, 86, 43, 0, 503, 504, 5, 33, 0, 0, 504, 506, 3, 86, 43, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 85, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 511, 3, 60, 30, 0, 511, 512, 5, 84, 0, 0, 512, 513, 3, 60, 30, 0, 513, 87, 1, 0, 0, 0, 514, 515, 5, 1, 0, 0, 515, 516, 3, 20, 10, 0, 516, 518, 3, 106, 53, 0, 517, 519, 3, 94, 47, 0, 518, 517, 1, 0, 0, 0, 518, 519, 1, 0, 0, 0, 519, 89, 1, 0, 0, 0, 520, 521, 5, 7, 0, 0, 521, 522, 3, 20, 10, 0, 522, 523, 3, 106, 53, 0, 523, 91, 1, 0, 0, 0, 524, 525, 5, 10, 0, 0, 525, 526, 3, 58, 29, 0, 526, 93, 1, 0, 0, 0, 527, 532, 3, 96, 48, 0, 528, 529, 5, 33, 0, 0, 529, 531, 3, 96, 48, 0, 530, 528, 1, 0, 0, 0, 531, 534, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 532, 533, 1, 0, 0, 0, 533, 95, 1, 0, 0, 0, 534, 532, 1, 0, 0, 0, 535, 536, 3, 64, 32, 0, 536, 537, 5, 31, 0, 0, 537, 538, 3, 68, 34, 0, 538, 97, 1, 0, 0, 0, 539, 540, 7, 6, 0, 0, 540, 99, 1, 0, 0, 0, 541, 544, 3, 102, 51, 0, 542, 544, 3, 104, 52, 0, 543, 541, 1, 0, 0, 0, 543, 542, 1, 0, 0, 0, 544, 101, 1, 0, 0, 0, 545, 547, 7, 0, 0, 0, 546, 545, 1, 0, 0, 0, 546, 547, 1, 0, 0, 0, 547, 548, 1, 0, 0, 0, 548, 549, 5, 27, 0, 0, 549, 103, 1, 0, 0, 0, 550, 552, 7, 0, 0, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 553, 1, 0, 0, 0, 553, 554, 5, 26, 0, 0, 554, 105, 1, 0, 0, 0, 555, 556, 5, 25, 0, 0, 556, 107, 1, 0, 0, 0, 557, 558, 7, 7, 0, 0, 558, 109, 1, 0, 0, 0, 559, 560, 5, 5, 0, 0, 560, 561, 3, 112, 56, 0, 561, 111, 1, 0, 0, 0, 562, 563, 5, 65, 0, 0, 563, 564, 3, 2, 1, 0, 564, 565, 5, 66, 0, 0, 565, 113, 1, 0, 0, 0, 566, 567, 5, 13, 0, 0, 567, 568, 5, 100, 0, 0, 568, 115, 1, 0, 0, 0, 569, 570, 5, 3, 0, 0, 570, 573, 5, 90, 0, 0, 571, 572, 5, 88, 0, 0, 572, 574, 3, 60, 30, 0, 573, 571, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 584, 1, 0, 0, 0, 575, 576, 5, 89, 0, 0, 576, 581, 3, 118, 59, 0, 577, 578, 5, 33, 0, 0, 578, 580, 3, 118, 59, 0, 579, 577, 1, 0, 0, 0, 580, 583, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 581, 582, 1, 0, 0, 0, 582, 585, 1, 0, 0, 0, 583, 581, 1, 0, 0, 0, 584, 575, 1, 0, 0, 0, 584, 585, 1, 0, 0, 0, 585, 117, 1, 0, 0, 0, 586, 587, 3, 60, 30, 0, 587, 588, 5, 31, 0, 0, 588, 590, 1, 0, 0, 0, 589, 586, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 591, 1, 0, 0, 0, 591, 592, 3, 60, 30, 0, 592, 119, 1, 0, 0, 0, 593, 594, 5, 18, 0, 0, 594, 595, 3, 36, 18, 0, 595, 596, 5, 88, 0, 0, 596, 597, 3, 62, 31, 0, 597, 121, 1, 0, 0, 0, 598, 599, 5, 17, 0, 0, 599, 602, 3, 54, 27, 0, 600, 601, 5, 28, 0, 0, 601, 603, 3, 30, 15, 0, 602, 600, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 123, 1, 0, 0, 0, 59, 135, 144, 162, 174, 183, 191, 197, 205, 207, 212, 219, 224, 235, 241, 249, 251, 262, 269, 280, 283, 289, 301, 307, 317, 321, 326, 336, 344, 357, 361, 365, 372, 376, 383, 389, 396, 404, 412, 420, 437, 448, 459, 464, 468, 473, 484, 489, 493, 507, 518, 532, 543, 546, 551, 573, 581, 584, 589, 602] \ No newline at end of file +[4, 1, 119, 603, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 134, 8, 1, 10, 1, 12, 1, 137, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 145, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 163, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 175, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 182, 8, 5, 10, 5, 12, 5, 185, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 192, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 198, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 206, 8, 5, 10, 5, 12, 5, 209, 9, 5, 1, 6, 1, 6, 3, 6, 213, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 220, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 225, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 236, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 242, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 250, 8, 9, 10, 9, 12, 9, 253, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 263, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 268, 8, 10, 10, 10, 12, 10, 271, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 279, 8, 11, 10, 11, 12, 11, 282, 9, 11, 3, 11, 284, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 5, 15, 298, 8, 15, 10, 15, 12, 15, 301, 9, 15, 1, 16, 1, 16, 1, 16, 3, 16, 306, 8, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 314, 8, 17, 10, 17, 12, 17, 317, 9, 17, 1, 17, 3, 17, 320, 8, 17, 1, 18, 1, 18, 1, 18, 3, 18, 325, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 335, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 5, 22, 341, 8, 22, 10, 22, 12, 22, 344, 9, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 354, 8, 24, 10, 24, 12, 24, 357, 9, 24, 1, 24, 3, 24, 360, 8, 24, 1, 24, 1, 24, 3, 24, 364, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 371, 8, 26, 1, 26, 1, 26, 3, 26, 375, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 380, 8, 27, 10, 27, 12, 27, 383, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 388, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 393, 8, 29, 10, 29, 12, 29, 396, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 401, 8, 30, 10, 30, 12, 30, 404, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 409, 8, 31, 10, 31, 12, 31, 412, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 419, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 434, 8, 34, 10, 34, 12, 34, 437, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 445, 8, 34, 10, 34, 12, 34, 448, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 456, 8, 34, 10, 34, 12, 34, 459, 9, 34, 1, 34, 1, 34, 3, 34, 463, 8, 34, 1, 35, 1, 35, 3, 35, 467, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 472, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 481, 8, 38, 10, 38, 12, 38, 484, 9, 38, 1, 39, 1, 39, 3, 39, 488, 8, 39, 1, 39, 1, 39, 3, 39, 492, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 504, 8, 42, 10, 42, 12, 42, 507, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 517, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 529, 8, 47, 10, 47, 12, 47, 532, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 542, 8, 50, 1, 51, 3, 51, 545, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 550, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 572, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 578, 8, 58, 10, 58, 12, 58, 581, 9, 58, 3, 58, 583, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 588, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 601, 8, 61, 1, 61, 0, 4, 2, 10, 18, 20, 62, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 0, 8, 1, 0, 59, 60, 1, 0, 61, 63, 2, 0, 26, 26, 76, 76, 1, 0, 67, 68, 2, 0, 31, 31, 35, 35, 2, 0, 38, 38, 41, 41, 2, 0, 37, 37, 51, 51, 2, 0, 52, 52, 54, 58, 628, 0, 124, 1, 0, 0, 0, 2, 127, 1, 0, 0, 0, 4, 144, 1, 0, 0, 0, 6, 162, 1, 0, 0, 0, 8, 164, 1, 0, 0, 0, 10, 197, 1, 0, 0, 0, 12, 224, 1, 0, 0, 0, 14, 226, 1, 0, 0, 0, 16, 235, 1, 0, 0, 0, 18, 241, 1, 0, 0, 0, 20, 262, 1, 0, 0, 0, 22, 272, 1, 0, 0, 0, 24, 287, 1, 0, 0, 0, 26, 289, 1, 0, 0, 0, 28, 291, 1, 0, 0, 0, 30, 294, 1, 0, 0, 0, 32, 305, 1, 0, 0, 0, 34, 309, 1, 0, 0, 0, 36, 324, 1, 0, 0, 0, 38, 328, 1, 0, 0, 0, 40, 330, 1, 0, 0, 0, 42, 334, 1, 0, 0, 0, 44, 336, 1, 0, 0, 0, 46, 345, 1, 0, 0, 0, 48, 349, 1, 0, 0, 0, 50, 365, 1, 0, 0, 0, 52, 368, 1, 0, 0, 0, 54, 376, 1, 0, 0, 0, 56, 384, 1, 0, 0, 0, 58, 389, 1, 0, 0, 0, 60, 397, 1, 0, 0, 0, 62, 405, 1, 0, 0, 0, 64, 413, 1, 0, 0, 0, 66, 418, 1, 0, 0, 0, 68, 462, 1, 0, 0, 0, 70, 466, 1, 0, 0, 0, 72, 471, 1, 0, 0, 0, 74, 473, 1, 0, 0, 0, 76, 476, 1, 0, 0, 0, 78, 485, 1, 0, 0, 0, 80, 493, 1, 0, 0, 0, 82, 496, 1, 0, 0, 0, 84, 499, 1, 0, 0, 0, 86, 508, 1, 0, 0, 0, 88, 512, 1, 0, 0, 0, 90, 518, 1, 0, 0, 0, 92, 522, 1, 0, 0, 0, 94, 525, 1, 0, 0, 0, 96, 533, 1, 0, 0, 0, 98, 537, 1, 0, 0, 0, 100, 541, 1, 0, 0, 0, 102, 544, 1, 0, 0, 0, 104, 549, 1, 0, 0, 0, 106, 553, 1, 0, 0, 0, 108, 555, 1, 0, 0, 0, 110, 557, 1, 0, 0, 0, 112, 560, 1, 0, 0, 0, 114, 564, 1, 0, 0, 0, 116, 567, 1, 0, 0, 0, 118, 587, 1, 0, 0, 0, 120, 591, 1, 0, 0, 0, 122, 596, 1, 0, 0, 0, 124, 125, 3, 2, 1, 0, 125, 126, 5, 0, 0, 1, 126, 1, 1, 0, 0, 0, 127, 128, 6, 1, -1, 0, 128, 129, 3, 4, 2, 0, 129, 135, 1, 0, 0, 0, 130, 131, 10, 1, 0, 0, 131, 132, 5, 25, 0, 0, 132, 134, 3, 6, 3, 0, 133, 130, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 135, 136, 1, 0, 0, 0, 136, 3, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 138, 145, 3, 110, 55, 0, 139, 145, 3, 34, 17, 0, 140, 145, 3, 28, 14, 0, 141, 145, 3, 114, 57, 0, 142, 143, 4, 2, 1, 0, 143, 145, 3, 48, 24, 0, 144, 138, 1, 0, 0, 0, 144, 139, 1, 0, 0, 0, 144, 140, 1, 0, 0, 0, 144, 141, 1, 0, 0, 0, 144, 142, 1, 0, 0, 0, 145, 5, 1, 0, 0, 0, 146, 163, 3, 50, 25, 0, 147, 163, 3, 8, 4, 0, 148, 163, 3, 80, 40, 0, 149, 163, 3, 74, 37, 0, 150, 163, 3, 52, 26, 0, 151, 163, 3, 76, 38, 0, 152, 163, 3, 82, 41, 0, 153, 163, 3, 84, 42, 0, 154, 163, 3, 88, 44, 0, 155, 163, 3, 90, 45, 0, 156, 163, 3, 116, 58, 0, 157, 163, 3, 92, 46, 0, 158, 159, 4, 3, 2, 0, 159, 163, 3, 122, 61, 0, 160, 161, 4, 3, 3, 0, 161, 163, 3, 120, 60, 0, 162, 146, 1, 0, 0, 0, 162, 147, 1, 0, 0, 0, 162, 148, 1, 0, 0, 0, 162, 149, 1, 0, 0, 0, 162, 150, 1, 0, 0, 0, 162, 151, 1, 0, 0, 0, 162, 152, 1, 0, 0, 0, 162, 153, 1, 0, 0, 0, 162, 154, 1, 0, 0, 0, 162, 155, 1, 0, 0, 0, 162, 156, 1, 0, 0, 0, 162, 157, 1, 0, 0, 0, 162, 158, 1, 0, 0, 0, 162, 160, 1, 0, 0, 0, 163, 7, 1, 0, 0, 0, 164, 165, 5, 16, 0, 0, 165, 166, 3, 10, 5, 0, 166, 9, 1, 0, 0, 0, 167, 168, 6, 5, -1, 0, 168, 169, 5, 44, 0, 0, 169, 198, 3, 10, 5, 8, 170, 198, 3, 16, 8, 0, 171, 198, 3, 12, 6, 0, 172, 174, 3, 16, 8, 0, 173, 175, 5, 44, 0, 0, 174, 173, 1, 0, 0, 0, 174, 175, 1, 0, 0, 0, 175, 176, 1, 0, 0, 0, 176, 177, 5, 39, 0, 0, 177, 178, 5, 43, 0, 0, 178, 183, 3, 16, 8, 0, 179, 180, 5, 34, 0, 0, 180, 182, 3, 16, 8, 0, 181, 179, 1, 0, 0, 0, 182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 186, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 186, 187, 5, 50, 0, 0, 187, 198, 1, 0, 0, 0, 188, 189, 3, 16, 8, 0, 189, 191, 5, 40, 0, 0, 190, 192, 5, 44, 0, 0, 191, 190, 1, 0, 0, 0, 191, 192, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 194, 5, 45, 0, 0, 194, 198, 1, 0, 0, 0, 195, 196, 4, 5, 4, 0, 196, 198, 3, 14, 7, 0, 197, 167, 1, 0, 0, 0, 197, 170, 1, 0, 0, 0, 197, 171, 1, 0, 0, 0, 197, 172, 1, 0, 0, 0, 197, 188, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 207, 1, 0, 0, 0, 199, 200, 10, 5, 0, 0, 200, 201, 5, 30, 0, 0, 201, 206, 3, 10, 5, 6, 202, 203, 10, 4, 0, 0, 203, 204, 5, 47, 0, 0, 204, 206, 3, 10, 5, 5, 205, 199, 1, 0, 0, 0, 205, 202, 1, 0, 0, 0, 206, 209, 1, 0, 0, 0, 207, 205, 1, 0, 0, 0, 207, 208, 1, 0, 0, 0, 208, 11, 1, 0, 0, 0, 209, 207, 1, 0, 0, 0, 210, 212, 3, 16, 8, 0, 211, 213, 5, 44, 0, 0, 212, 211, 1, 0, 0, 0, 212, 213, 1, 0, 0, 0, 213, 214, 1, 0, 0, 0, 214, 215, 5, 42, 0, 0, 215, 216, 3, 106, 53, 0, 216, 225, 1, 0, 0, 0, 217, 219, 3, 16, 8, 0, 218, 220, 5, 44, 0, 0, 219, 218, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 222, 5, 49, 0, 0, 222, 223, 3, 106, 53, 0, 223, 225, 1, 0, 0, 0, 224, 210, 1, 0, 0, 0, 224, 217, 1, 0, 0, 0, 225, 13, 1, 0, 0, 0, 226, 227, 3, 58, 29, 0, 227, 228, 5, 24, 0, 0, 228, 229, 3, 68, 34, 0, 229, 15, 1, 0, 0, 0, 230, 236, 3, 18, 9, 0, 231, 232, 3, 18, 9, 0, 232, 233, 3, 108, 54, 0, 233, 234, 3, 18, 9, 0, 234, 236, 1, 0, 0, 0, 235, 230, 1, 0, 0, 0, 235, 231, 1, 0, 0, 0, 236, 17, 1, 0, 0, 0, 237, 238, 6, 9, -1, 0, 238, 242, 3, 20, 10, 0, 239, 240, 7, 0, 0, 0, 240, 242, 3, 18, 9, 3, 241, 237, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 251, 1, 0, 0, 0, 243, 244, 10, 2, 0, 0, 244, 245, 7, 1, 0, 0, 245, 250, 3, 18, 9, 3, 246, 247, 10, 1, 0, 0, 247, 248, 7, 0, 0, 0, 248, 250, 3, 18, 9, 2, 249, 243, 1, 0, 0, 0, 249, 246, 1, 0, 0, 0, 250, 253, 1, 0, 0, 0, 251, 249, 1, 0, 0, 0, 251, 252, 1, 0, 0, 0, 252, 19, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 254, 255, 6, 10, -1, 0, 255, 263, 3, 68, 34, 0, 256, 263, 3, 58, 29, 0, 257, 263, 3, 22, 11, 0, 258, 259, 5, 43, 0, 0, 259, 260, 3, 10, 5, 0, 260, 261, 5, 50, 0, 0, 261, 263, 1, 0, 0, 0, 262, 254, 1, 0, 0, 0, 262, 256, 1, 0, 0, 0, 262, 257, 1, 0, 0, 0, 262, 258, 1, 0, 0, 0, 263, 269, 1, 0, 0, 0, 264, 265, 10, 1, 0, 0, 265, 266, 5, 33, 0, 0, 266, 268, 3, 26, 13, 0, 267, 264, 1, 0, 0, 0, 268, 271, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 21, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 272, 273, 3, 24, 12, 0, 273, 283, 5, 43, 0, 0, 274, 284, 5, 61, 0, 0, 275, 280, 3, 10, 5, 0, 276, 277, 5, 34, 0, 0, 277, 279, 3, 10, 5, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 274, 1, 0, 0, 0, 283, 275, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 286, 5, 50, 0, 0, 286, 23, 1, 0, 0, 0, 287, 288, 3, 72, 36, 0, 288, 25, 1, 0, 0, 0, 289, 290, 3, 64, 32, 0, 290, 27, 1, 0, 0, 0, 291, 292, 5, 12, 0, 0, 292, 293, 3, 30, 15, 0, 293, 29, 1, 0, 0, 0, 294, 299, 3, 32, 16, 0, 295, 296, 5, 34, 0, 0, 296, 298, 3, 32, 16, 0, 297, 295, 1, 0, 0, 0, 298, 301, 1, 0, 0, 0, 299, 297, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 31, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 302, 303, 3, 58, 29, 0, 303, 304, 5, 32, 0, 0, 304, 306, 1, 0, 0, 0, 305, 302, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 1, 0, 0, 0, 307, 308, 3, 10, 5, 0, 308, 33, 1, 0, 0, 0, 309, 310, 5, 6, 0, 0, 310, 315, 3, 36, 18, 0, 311, 312, 5, 34, 0, 0, 312, 314, 3, 36, 18, 0, 313, 311, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 318, 320, 3, 42, 21, 0, 319, 318, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 35, 1, 0, 0, 0, 321, 322, 3, 38, 19, 0, 322, 323, 5, 24, 0, 0, 323, 325, 1, 0, 0, 0, 324, 321, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 1, 0, 0, 0, 326, 327, 3, 40, 20, 0, 327, 37, 1, 0, 0, 0, 328, 329, 5, 76, 0, 0, 329, 39, 1, 0, 0, 0, 330, 331, 7, 2, 0, 0, 331, 41, 1, 0, 0, 0, 332, 335, 3, 44, 22, 0, 333, 335, 3, 46, 23, 0, 334, 332, 1, 0, 0, 0, 334, 333, 1, 0, 0, 0, 335, 43, 1, 0, 0, 0, 336, 337, 5, 75, 0, 0, 337, 342, 5, 76, 0, 0, 338, 339, 5, 34, 0, 0, 339, 341, 5, 76, 0, 0, 340, 338, 1, 0, 0, 0, 341, 344, 1, 0, 0, 0, 342, 340, 1, 0, 0, 0, 342, 343, 1, 0, 0, 0, 343, 45, 1, 0, 0, 0, 344, 342, 1, 0, 0, 0, 345, 346, 5, 65, 0, 0, 346, 347, 3, 44, 22, 0, 347, 348, 5, 66, 0, 0, 348, 47, 1, 0, 0, 0, 349, 350, 5, 19, 0, 0, 350, 355, 3, 36, 18, 0, 351, 352, 5, 34, 0, 0, 352, 354, 3, 36, 18, 0, 353, 351, 1, 0, 0, 0, 354, 357, 1, 0, 0, 0, 355, 353, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 359, 1, 0, 0, 0, 357, 355, 1, 0, 0, 0, 358, 360, 3, 54, 27, 0, 359, 358, 1, 0, 0, 0, 359, 360, 1, 0, 0, 0, 360, 363, 1, 0, 0, 0, 361, 362, 5, 29, 0, 0, 362, 364, 3, 30, 15, 0, 363, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 49, 1, 0, 0, 0, 365, 366, 5, 4, 0, 0, 366, 367, 3, 30, 15, 0, 367, 51, 1, 0, 0, 0, 368, 370, 5, 15, 0, 0, 369, 371, 3, 54, 27, 0, 370, 369, 1, 0, 0, 0, 370, 371, 1, 0, 0, 0, 371, 374, 1, 0, 0, 0, 372, 373, 5, 29, 0, 0, 373, 375, 3, 30, 15, 0, 374, 372, 1, 0, 0, 0, 374, 375, 1, 0, 0, 0, 375, 53, 1, 0, 0, 0, 376, 381, 3, 56, 28, 0, 377, 378, 5, 34, 0, 0, 378, 380, 3, 56, 28, 0, 379, 377, 1, 0, 0, 0, 380, 383, 1, 0, 0, 0, 381, 379, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 55, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 384, 387, 3, 32, 16, 0, 385, 386, 5, 16, 0, 0, 386, 388, 3, 10, 5, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 57, 1, 0, 0, 0, 389, 394, 3, 72, 36, 0, 390, 391, 5, 36, 0, 0, 391, 393, 3, 72, 36, 0, 392, 390, 1, 0, 0, 0, 393, 396, 1, 0, 0, 0, 394, 392, 1, 0, 0, 0, 394, 395, 1, 0, 0, 0, 395, 59, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 397, 402, 3, 66, 33, 0, 398, 399, 5, 36, 0, 0, 399, 401, 3, 66, 33, 0, 400, 398, 1, 0, 0, 0, 401, 404, 1, 0, 0, 0, 402, 400, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 61, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 405, 410, 3, 60, 30, 0, 406, 407, 5, 34, 0, 0, 407, 409, 3, 60, 30, 0, 408, 406, 1, 0, 0, 0, 409, 412, 1, 0, 0, 0, 410, 408, 1, 0, 0, 0, 410, 411, 1, 0, 0, 0, 411, 63, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 413, 414, 7, 3, 0, 0, 414, 65, 1, 0, 0, 0, 415, 419, 5, 80, 0, 0, 416, 417, 4, 33, 10, 0, 417, 419, 3, 70, 35, 0, 418, 415, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 419, 67, 1, 0, 0, 0, 420, 463, 5, 45, 0, 0, 421, 422, 3, 104, 52, 0, 422, 423, 5, 67, 0, 0, 423, 463, 1, 0, 0, 0, 424, 463, 3, 102, 51, 0, 425, 463, 3, 104, 52, 0, 426, 463, 3, 98, 49, 0, 427, 463, 3, 70, 35, 0, 428, 463, 3, 106, 53, 0, 429, 430, 5, 65, 0, 0, 430, 435, 3, 100, 50, 0, 431, 432, 5, 34, 0, 0, 432, 434, 3, 100, 50, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 438, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 5, 66, 0, 0, 439, 463, 1, 0, 0, 0, 440, 441, 5, 65, 0, 0, 441, 446, 3, 98, 49, 0, 442, 443, 5, 34, 0, 0, 443, 445, 3, 98, 49, 0, 444, 442, 1, 0, 0, 0, 445, 448, 1, 0, 0, 0, 446, 444, 1, 0, 0, 0, 446, 447, 1, 0, 0, 0, 447, 449, 1, 0, 0, 0, 448, 446, 1, 0, 0, 0, 449, 450, 5, 66, 0, 0, 450, 463, 1, 0, 0, 0, 451, 452, 5, 65, 0, 0, 452, 457, 3, 106, 53, 0, 453, 454, 5, 34, 0, 0, 454, 456, 3, 106, 53, 0, 455, 453, 1, 0, 0, 0, 456, 459, 1, 0, 0, 0, 457, 455, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 460, 1, 0, 0, 0, 459, 457, 1, 0, 0, 0, 460, 461, 5, 66, 0, 0, 461, 463, 1, 0, 0, 0, 462, 420, 1, 0, 0, 0, 462, 421, 1, 0, 0, 0, 462, 424, 1, 0, 0, 0, 462, 425, 1, 0, 0, 0, 462, 426, 1, 0, 0, 0, 462, 427, 1, 0, 0, 0, 462, 428, 1, 0, 0, 0, 462, 429, 1, 0, 0, 0, 462, 440, 1, 0, 0, 0, 462, 451, 1, 0, 0, 0, 463, 69, 1, 0, 0, 0, 464, 467, 5, 48, 0, 0, 465, 467, 5, 64, 0, 0, 466, 464, 1, 0, 0, 0, 466, 465, 1, 0, 0, 0, 467, 71, 1, 0, 0, 0, 468, 472, 3, 64, 32, 0, 469, 470, 4, 36, 11, 0, 470, 472, 3, 70, 35, 0, 471, 468, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 472, 73, 1, 0, 0, 0, 473, 474, 5, 9, 0, 0, 474, 475, 5, 27, 0, 0, 475, 75, 1, 0, 0, 0, 476, 477, 5, 14, 0, 0, 477, 482, 3, 78, 39, 0, 478, 479, 5, 34, 0, 0, 479, 481, 3, 78, 39, 0, 480, 478, 1, 0, 0, 0, 481, 484, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 77, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 485, 487, 3, 10, 5, 0, 486, 488, 7, 4, 0, 0, 487, 486, 1, 0, 0, 0, 487, 488, 1, 0, 0, 0, 488, 491, 1, 0, 0, 0, 489, 490, 5, 46, 0, 0, 490, 492, 7, 5, 0, 0, 491, 489, 1, 0, 0, 0, 491, 492, 1, 0, 0, 0, 492, 79, 1, 0, 0, 0, 493, 494, 5, 8, 0, 0, 494, 495, 3, 62, 31, 0, 495, 81, 1, 0, 0, 0, 496, 497, 5, 2, 0, 0, 497, 498, 3, 62, 31, 0, 498, 83, 1, 0, 0, 0, 499, 500, 5, 11, 0, 0, 500, 505, 3, 86, 43, 0, 501, 502, 5, 34, 0, 0, 502, 504, 3, 86, 43, 0, 503, 501, 1, 0, 0, 0, 504, 507, 1, 0, 0, 0, 505, 503, 1, 0, 0, 0, 505, 506, 1, 0, 0, 0, 506, 85, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 508, 509, 3, 60, 30, 0, 509, 510, 5, 84, 0, 0, 510, 511, 3, 60, 30, 0, 511, 87, 1, 0, 0, 0, 512, 513, 5, 1, 0, 0, 513, 514, 3, 20, 10, 0, 514, 516, 3, 106, 53, 0, 515, 517, 3, 94, 47, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 89, 1, 0, 0, 0, 518, 519, 5, 7, 0, 0, 519, 520, 3, 20, 10, 0, 520, 521, 3, 106, 53, 0, 521, 91, 1, 0, 0, 0, 522, 523, 5, 10, 0, 0, 523, 524, 3, 58, 29, 0, 524, 93, 1, 0, 0, 0, 525, 530, 3, 96, 48, 0, 526, 527, 5, 34, 0, 0, 527, 529, 3, 96, 48, 0, 528, 526, 1, 0, 0, 0, 529, 532, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 95, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 533, 534, 3, 64, 32, 0, 534, 535, 5, 32, 0, 0, 535, 536, 3, 68, 34, 0, 536, 97, 1, 0, 0, 0, 537, 538, 7, 6, 0, 0, 538, 99, 1, 0, 0, 0, 539, 542, 3, 102, 51, 0, 540, 542, 3, 104, 52, 0, 541, 539, 1, 0, 0, 0, 541, 540, 1, 0, 0, 0, 542, 101, 1, 0, 0, 0, 543, 545, 7, 0, 0, 0, 544, 543, 1, 0, 0, 0, 544, 545, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 547, 5, 28, 0, 0, 547, 103, 1, 0, 0, 0, 548, 550, 7, 0, 0, 0, 549, 548, 1, 0, 0, 0, 549, 550, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 552, 5, 27, 0, 0, 552, 105, 1, 0, 0, 0, 553, 554, 5, 26, 0, 0, 554, 107, 1, 0, 0, 0, 555, 556, 7, 7, 0, 0, 556, 109, 1, 0, 0, 0, 557, 558, 5, 5, 0, 0, 558, 559, 3, 112, 56, 0, 559, 111, 1, 0, 0, 0, 560, 561, 5, 65, 0, 0, 561, 562, 3, 2, 1, 0, 562, 563, 5, 66, 0, 0, 563, 113, 1, 0, 0, 0, 564, 565, 5, 13, 0, 0, 565, 566, 5, 100, 0, 0, 566, 115, 1, 0, 0, 0, 567, 568, 5, 3, 0, 0, 568, 571, 5, 90, 0, 0, 569, 570, 5, 88, 0, 0, 570, 572, 3, 60, 30, 0, 571, 569, 1, 0, 0, 0, 571, 572, 1, 0, 0, 0, 572, 582, 1, 0, 0, 0, 573, 574, 5, 89, 0, 0, 574, 579, 3, 118, 59, 0, 575, 576, 5, 34, 0, 0, 576, 578, 3, 118, 59, 0, 577, 575, 1, 0, 0, 0, 578, 581, 1, 0, 0, 0, 579, 577, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 583, 1, 0, 0, 0, 581, 579, 1, 0, 0, 0, 582, 573, 1, 0, 0, 0, 582, 583, 1, 0, 0, 0, 583, 117, 1, 0, 0, 0, 584, 585, 3, 60, 30, 0, 585, 586, 5, 32, 0, 0, 586, 588, 1, 0, 0, 0, 587, 584, 1, 0, 0, 0, 587, 588, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 590, 3, 60, 30, 0, 590, 119, 1, 0, 0, 0, 591, 592, 5, 18, 0, 0, 592, 593, 3, 36, 18, 0, 593, 594, 5, 88, 0, 0, 594, 595, 3, 62, 31, 0, 595, 121, 1, 0, 0, 0, 596, 597, 5, 17, 0, 0, 597, 600, 3, 54, 27, 0, 598, 599, 5, 29, 0, 0, 599, 601, 3, 30, 15, 0, 600, 598, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 123, 1, 0, 0, 0, 58, 135, 144, 162, 174, 183, 191, 197, 205, 207, 212, 219, 224, 235, 241, 249, 251, 262, 269, 280, 283, 299, 305, 315, 319, 324, 334, 342, 355, 359, 363, 370, 374, 381, 387, 394, 402, 410, 418, 435, 446, 457, 462, 466, 471, 482, 487, 491, 505, 516, 530, 541, 544, 549, 571, 579, 582, 587, 600] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens index 4d1f42628914..3dd1a2c75403 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens @@ -21,46 +21,46 @@ UNKNOWN_CMD=20 LINE_COMMENT=21 MULTILINE_COMMENT=22 WS=23 -PIPE=24 -QUOTED_STRING=25 -INTEGER_LITERAL=26 -DECIMAL_LITERAL=27 -BY=28 -AND=29 -ASC=30 -ASSIGN=31 -CAST_OP=32 -COMMA=33 -DESC=34 -DOT=35 -FALSE=36 -FIRST=37 -IN=38 -IS=39 -LAST=40 -LIKE=41 -LP=42 -NOT=43 -NULL=44 -NULLS=45 -OR=46 -PARAM=47 -RLIKE=48 -RP=49 -TRUE=50 -EQ=51 -CIEQ=52 -NEQ=53 -LT=54 -LTE=55 -GT=56 -GTE=57 -PLUS=58 -MINUS=59 -ASTERISK=60 -SLASH=61 -PERCENT=62 -MATCH=63 +COLON=24 +PIPE=25 +QUOTED_STRING=26 +INTEGER_LITERAL=27 +DECIMAL_LITERAL=28 +BY=29 +AND=30 +ASC=31 +ASSIGN=32 +CAST_OP=33 +COMMA=34 +DESC=35 +DOT=36 +FALSE=37 +FIRST=38 +IN=39 +IS=40 +LAST=41 +LIKE=42 +LP=43 +NOT=44 +NULL=45 +NULLS=46 +OR=47 +PARAM=48 +RLIKE=49 +RP=50 +TRUE=51 +EQ=52 +CIEQ=53 +NEQ=54 +LT=55 +LTE=56 +GT=57 +GTE=58 +PLUS=59 +MINUS=60 +ASTERISK=61 +SLASH=62 +PERCENT=63 NAMED_OR_POSITIONAL_PARAM=64 OPENING_BRACKET=65 CLOSING_BRACKET=66 @@ -101,23 +101,22 @@ INFO=100 SHOW_LINE_COMMENT=101 SHOW_MULTILINE_COMMENT=102 SHOW_WS=103 -COLON=104 -SETTING=105 -SETTING_LINE_COMMENT=106 -SETTTING_MULTILINE_COMMENT=107 -SETTING_WS=108 -LOOKUP_LINE_COMMENT=109 -LOOKUP_MULTILINE_COMMENT=110 -LOOKUP_WS=111 -LOOKUP_FIELD_LINE_COMMENT=112 -LOOKUP_FIELD_MULTILINE_COMMENT=113 -LOOKUP_FIELD_WS=114 -METRICS_LINE_COMMENT=115 -METRICS_MULTILINE_COMMENT=116 -METRICS_WS=117 -CLOSING_METRICS_LINE_COMMENT=118 -CLOSING_METRICS_MULTILINE_COMMENT=119 -CLOSING_METRICS_WS=120 +SETTING=104 +SETTING_LINE_COMMENT=105 +SETTTING_MULTILINE_COMMENT=106 +SETTING_WS=107 +LOOKUP_LINE_COMMENT=108 +LOOKUP_MULTILINE_COMMENT=109 +LOOKUP_WS=110 +LOOKUP_FIELD_LINE_COMMENT=111 +LOOKUP_FIELD_MULTILINE_COMMENT=112 +LOOKUP_FIELD_WS=113 +METRICS_LINE_COMMENT=114 +METRICS_MULTILINE_COMMENT=115 +METRICS_WS=116 +CLOSING_METRICS_LINE_COMMENT=117 +CLOSING_METRICS_MULTILINE_COMMENT=118 +CLOSING_METRICS_WS=119 'dissect'=1 'drop'=2 'enrich'=3 @@ -134,47 +133,46 @@ CLOSING_METRICS_WS=120 'sort'=14 'stats'=15 'where'=16 -'|'=24 -'by'=28 -'and'=29 -'asc'=30 -'='=31 -'::'=32 -','=33 -'desc'=34 -'.'=35 -'false'=36 -'first'=37 -'in'=38 -'is'=39 -'last'=40 -'like'=41 -'('=42 -'not'=43 -'null'=44 -'nulls'=45 -'or'=46 -'?'=47 -'rlike'=48 -')'=49 -'true'=50 -'=='=51 -'=~'=52 -'!='=53 -'<'=54 -'<='=55 -'>'=56 -'>='=57 -'+'=58 -'-'=59 -'*'=60 -'/'=61 -'%'=62 -'match'=63 +':'=24 +'|'=25 +'by'=29 +'and'=30 +'asc'=31 +'='=32 +'::'=33 +','=34 +'desc'=35 +'.'=36 +'false'=37 +'first'=38 +'in'=39 +'is'=40 +'last'=41 +'like'=42 +'('=43 +'not'=44 +'null'=45 +'nulls'=46 +'or'=47 +'?'=48 +'rlike'=49 +')'=50 +'true'=51 +'=='=52 +'=~'=53 +'!='=54 +'<'=55 +'<='=56 +'>'=57 +'>='=58 +'+'=59 +'-'=60 +'*'=61 +'/'=62 +'%'=63 ']'=66 'metadata'=75 'as'=84 'on'=88 'with'=89 'info'=100 -':'=104 diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.ts b/packages/kbn-esql-ast/src/antlr/esql_parser.ts index b0af12e1ebc1..4dc0c5c628e3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.ts @@ -51,46 +51,46 @@ export default class esql_parser extends parser_config { public static readonly LINE_COMMENT = 21; public static readonly MULTILINE_COMMENT = 22; public static readonly WS = 23; - public static readonly PIPE = 24; - public static readonly QUOTED_STRING = 25; - public static readonly INTEGER_LITERAL = 26; - public static readonly DECIMAL_LITERAL = 27; - public static readonly BY = 28; - public static readonly AND = 29; - public static readonly ASC = 30; - public static readonly ASSIGN = 31; - public static readonly CAST_OP = 32; - public static readonly COMMA = 33; - public static readonly DESC = 34; - public static readonly DOT = 35; - public static readonly FALSE = 36; - public static readonly FIRST = 37; - public static readonly IN = 38; - public static readonly IS = 39; - public static readonly LAST = 40; - public static readonly LIKE = 41; - public static readonly LP = 42; - public static readonly NOT = 43; - public static readonly NULL = 44; - public static readonly NULLS = 45; - public static readonly OR = 46; - public static readonly PARAM = 47; - public static readonly RLIKE = 48; - public static readonly RP = 49; - public static readonly TRUE = 50; - public static readonly EQ = 51; - public static readonly CIEQ = 52; - public static readonly NEQ = 53; - public static readonly LT = 54; - public static readonly LTE = 55; - public static readonly GT = 56; - public static readonly GTE = 57; - public static readonly PLUS = 58; - public static readonly MINUS = 59; - public static readonly ASTERISK = 60; - public static readonly SLASH = 61; - public static readonly PERCENT = 62; - public static readonly MATCH = 63; + public static readonly COLON = 24; + public static readonly PIPE = 25; + public static readonly QUOTED_STRING = 26; + public static readonly INTEGER_LITERAL = 27; + public static readonly DECIMAL_LITERAL = 28; + public static readonly BY = 29; + public static readonly AND = 30; + public static readonly ASC = 31; + public static readonly ASSIGN = 32; + public static readonly CAST_OP = 33; + public static readonly COMMA = 34; + public static readonly DESC = 35; + public static readonly DOT = 36; + public static readonly FALSE = 37; + public static readonly FIRST = 38; + public static readonly IN = 39; + public static readonly IS = 40; + public static readonly LAST = 41; + public static readonly LIKE = 42; + public static readonly LP = 43; + public static readonly NOT = 44; + public static readonly NULL = 45; + public static readonly NULLS = 46; + public static readonly OR = 47; + public static readonly PARAM = 48; + public static readonly RLIKE = 49; + public static readonly RP = 50; + public static readonly TRUE = 51; + public static readonly EQ = 52; + public static readonly CIEQ = 53; + public static readonly NEQ = 54; + public static readonly LT = 55; + public static readonly LTE = 56; + public static readonly GT = 57; + public static readonly GTE = 58; + public static readonly PLUS = 59; + public static readonly MINUS = 60; + public static readonly ASTERISK = 61; + public static readonly SLASH = 62; + public static readonly PERCENT = 63; public static readonly NAMED_OR_POSITIONAL_PARAM = 64; public static readonly OPENING_BRACKET = 65; public static readonly CLOSING_BRACKET = 66; @@ -131,23 +131,22 @@ export default class esql_parser extends parser_config { public static readonly SHOW_LINE_COMMENT = 101; public static readonly SHOW_MULTILINE_COMMENT = 102; public static readonly SHOW_WS = 103; - public static readonly COLON = 104; - public static readonly SETTING = 105; - public static readonly SETTING_LINE_COMMENT = 106; - public static readonly SETTTING_MULTILINE_COMMENT = 107; - public static readonly SETTING_WS = 108; - public static readonly LOOKUP_LINE_COMMENT = 109; - public static readonly LOOKUP_MULTILINE_COMMENT = 110; - public static readonly LOOKUP_WS = 111; - public static readonly LOOKUP_FIELD_LINE_COMMENT = 112; - public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 113; - public static readonly LOOKUP_FIELD_WS = 114; - public static readonly METRICS_LINE_COMMENT = 115; - public static readonly METRICS_MULTILINE_COMMENT = 116; - public static readonly METRICS_WS = 117; - public static readonly CLOSING_METRICS_LINE_COMMENT = 118; - public static readonly CLOSING_METRICS_MULTILINE_COMMENT = 119; - public static readonly CLOSING_METRICS_WS = 120; + public static readonly SETTING = 104; + public static readonly SETTING_LINE_COMMENT = 105; + public static readonly SETTTING_MULTILINE_COMMENT = 106; + public static readonly SETTING_WS = 107; + public static readonly LOOKUP_LINE_COMMENT = 108; + public static readonly LOOKUP_MULTILINE_COMMENT = 109; + public static readonly LOOKUP_WS = 110; + public static readonly LOOKUP_FIELD_LINE_COMMENT = 111; + public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 112; + public static readonly LOOKUP_FIELD_WS = 113; + public static readonly METRICS_LINE_COMMENT = 114; + public static readonly METRICS_MULTILINE_COMMENT = 115; + public static readonly METRICS_WS = 116; + public static readonly CLOSING_METRICS_LINE_COMMENT = 117; + public static readonly CLOSING_METRICS_MULTILINE_COMMENT = 118; + public static readonly CLOSING_METRICS_WS = 119; public static override readonly EOF = Token.EOF; public static readonly RULE_singleStatement = 0; public static readonly RULE_query = 1; @@ -224,26 +223,26 @@ export default class esql_parser extends parser_config { null, null, null, null, null, null, - "'|'", null, + "':'", "'|'", null, null, - "'by'", "'and'", - "'asc'", "'='", - "'::'", "','", - "'desc'", "'.'", - "'false'", "'first'", - "'in'", "'is'", - "'last'", "'like'", - "'('", "'not'", - "'null'", "'nulls'", - "'or'", "'?'", - "'rlike'", "')'", - "'true'", "'=='", - "'=~'", "'!='", - "'<'", "'<='", - "'>'", "'>='", - "'+'", "'-'", - "'*'", "'/'", - "'%'", "'match'", + null, "'by'", + "'and'", "'asc'", + "'='", "'::'", + "','", "'desc'", + "'.'", "'false'", + "'first'", "'in'", + "'is'", "'last'", + "'like'", "'('", + "'not'", "'null'", + "'nulls'", "'or'", + "'?'", "'rlike'", + "')'", "'true'", + "'=='", "'=~'", + "'!='", "'<'", + "'<='", "'>'", + "'>='", "'+'", + "'-'", "'*'", + "'/'", "'%'", null, null, "']'", null, null, null, @@ -262,9 +261,7 @@ export default class esql_parser extends parser_config { null, null, null, null, null, null, - "'info'", null, - null, null, - "':'" ]; + "'info'" ]; public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", @@ -280,8 +277,8 @@ export default class esql_parser extends parser_config { "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", - "WS", "PIPE", - "QUOTED_STRING", + "WS", "COLON", + "PIPE", "QUOTED_STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", @@ -302,7 +299,7 @@ export default class esql_parser extends parser_config { "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "MATCH", "NAMED_OR_POSITIONAL_PARAM", + "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", @@ -339,7 +336,7 @@ export default class esql_parser extends parser_config { "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", "SHOW_WS", - "COLON", "SETTING", + "SETTING", "SETTING_LINE_COMMENT", "SETTTING_MULTILINE_COMMENT", "SETTING_WS", @@ -767,7 +764,7 @@ export default class esql_parser extends parser_config { this.state = 174; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===43) { + if (_la===44) { { this.state = 173; this.match(esql_parser.NOT); @@ -783,7 +780,7 @@ export default class esql_parser extends parser_config { this.state = 183; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===33) { + while (_la===34) { { { this.state = 179; @@ -812,7 +809,7 @@ export default class esql_parser extends parser_config { this.state = 191; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===43) { + if (_la===44) { { this.state = 190; this.match(esql_parser.NOT); @@ -921,7 +918,7 @@ export default class esql_parser extends parser_config { this.state = 212; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===43) { + if (_la===44) { { this.state = 211; this.match(esql_parser.NOT); @@ -942,7 +939,7 @@ export default class esql_parser extends parser_config { this.state = 219; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===43) { + if (_la===44) { { this.state = 218; this.match(esql_parser.NOT); @@ -979,11 +976,11 @@ export default class esql_parser extends parser_config { this.enterOuterAlt(localctx, 1); { this.state = 226; - this.valueExpression(); + localctx._fieldExp = this.qualifiedName(); this.state = 227; - this.match(esql_parser.MATCH); + this.match(esql_parser.COLON); this.state = 228; - localctx._queryString = this.string_(); + localctx._queryString = this.constant(); } } catch (re) { @@ -1085,7 +1082,7 @@ export default class esql_parser extends parser_config { this.state = 239; (localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===58 || _la===59)) { + if(!(_la===59 || _la===60)) { (localctx as ArithmeticUnaryContext)._operator = this._errHandler.recoverInline(this); } else { @@ -1123,7 +1120,7 @@ export default class esql_parser extends parser_config { this.state = 244; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 60)) & ~0x1F) === 0 && ((1 << (_la - 60)) & 7) !== 0))) { + if(!(((((_la - 61)) & ~0x1F) === 0 && ((1 << (_la - 61)) & 7) !== 0))) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { @@ -1146,7 +1143,7 @@ export default class esql_parser extends parser_config { this.state = 247; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===58 || _la===59)) { + if(!(_la===59 || _la===60)) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { @@ -1318,7 +1315,7 @@ export default class esql_parser extends parser_config { this.state = 280; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===33) { + while (_la===34) { { { this.state = 276; @@ -1358,23 +1355,10 @@ export default class esql_parser extends parser_config { let localctx: FunctionNameContext = new FunctionNameContext(this, this._ctx, this.state); this.enterRule(localctx, 24, esql_parser.RULE_functionName); try { - this.state = 289; - this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 20, this._ctx) ) { - case 1: - this.enterOuterAlt(localctx, 1); - { - this.state = 287; - this.match(esql_parser.MATCH); - } - break; - case 2: - this.enterOuterAlt(localctx, 2); - { - this.state = 288; - this.identifierOrParameter(); - } - break; + this.enterOuterAlt(localctx, 1); + { + this.state = 287; + this.identifierOrParameter(); } } catch (re) { @@ -1399,7 +1383,7 @@ export default class esql_parser extends parser_config { localctx = new ToDataTypeContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 291; + this.state = 289; this.identifier(); } } @@ -1424,9 +1408,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 293; + this.state = 291; this.match(esql_parser.ROW); - this.state = 294; + this.state = 292; this.fields(); } } @@ -1452,25 +1436,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 296; + this.state = 294; this.field(); - this.state = 301; + this.state = 299; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 297; + this.state = 295; this.match(esql_parser.COMMA); - this.state = 298; + this.state = 296; this.field(); } } } - this.state = 303; + this.state = 301; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 21, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); } } } @@ -1495,19 +1479,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 307; + this.state = 305; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 22, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 21, this._ctx) ) { case 1: { - this.state = 304; + this.state = 302; this.qualifiedName(); - this.state = 305; + this.state = 303; this.match(esql_parser.ASSIGN); } break; } - this.state = 309; + this.state = 307; this.booleanExpression(0); } } @@ -1533,34 +1517,34 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 311; + this.state = 309; this.match(esql_parser.FROM); - this.state = 312; + this.state = 310; this.indexPattern(); - this.state = 317; + this.state = 315; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 313; + this.state = 311; this.match(esql_parser.COMMA); - this.state = 314; + this.state = 312; this.indexPattern(); } } } - this.state = 319; + this.state = 317; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); } - this.state = 321; + this.state = 319; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 23, this._ctx) ) { case 1: { - this.state = 320; + this.state = 318; this.metadata(); } break; @@ -1588,19 +1572,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 326; + this.state = 324; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 25, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { case 1: { - this.state = 323; + this.state = 321; this.clusterString(); - this.state = 324; + this.state = 322; this.match(esql_parser.COLON); } break; } - this.state = 328; + this.state = 326; this.indexString(); } } @@ -1625,7 +1609,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 330; + this.state = 328; this.match(esql_parser.UNQUOTED_SOURCE); } } @@ -1651,9 +1635,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 332; + this.state = 330; _la = this._input.LA(1); - if(!(_la===25 || _la===76)) { + if(!(_la===26 || _la===76)) { this._errHandler.recoverInline(this); } else { @@ -1681,20 +1665,20 @@ export default class esql_parser extends parser_config { let localctx: MetadataContext = new MetadataContext(this, this._ctx, this.state); this.enterRule(localctx, 42, esql_parser.RULE_metadata); try { - this.state = 336; + this.state = 334; this._errHandler.sync(this); switch (this._input.LA(1)) { case 75: this.enterOuterAlt(localctx, 1); { - this.state = 334; + this.state = 332; this.metadataOption(); } break; case 65: this.enterOuterAlt(localctx, 2); { - this.state = 335; + this.state = 333; this.deprecated_metadata(); } break; @@ -1724,27 +1708,27 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 338; + this.state = 336; this.match(esql_parser.METADATA); - this.state = 339; + this.state = 337; this.match(esql_parser.UNQUOTED_SOURCE); - this.state = 344; + this.state = 342; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 340; + this.state = 338; this.match(esql_parser.COMMA); - this.state = 341; + this.state = 339; this.match(esql_parser.UNQUOTED_SOURCE); } } } - this.state = 346; + this.state = 344; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 26, this._ctx); } } } @@ -1769,11 +1753,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 347; + this.state = 345; this.match(esql_parser.OPENING_BRACKET); - this.state = 348; + this.state = 346; this.metadataOption(); - this.state = 349; + this.state = 347; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1799,46 +1783,46 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 351; + this.state = 349; this.match(esql_parser.DEV_METRICS); - this.state = 352; + this.state = 350; this.indexPattern(); - this.state = 357; + this.state = 355; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 353; + this.state = 351; this.match(esql_parser.COMMA); - this.state = 354; + this.state = 352; this.indexPattern(); } } } - this.state = 359; + this.state = 357; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); } - this.state = 361; + this.state = 359; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { case 1: { - this.state = 360; + this.state = 358; localctx._aggregates = this.aggFields(); } break; } - this.state = 365; + this.state = 363; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { case 1: { - this.state = 363; + this.state = 361; this.match(esql_parser.BY); - this.state = 364; + this.state = 362; localctx._grouping = this.fields(); } break; @@ -1866,9 +1850,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 367; + this.state = 365; this.match(esql_parser.EVAL); - this.state = 368; + this.state = 366; this.fields(); } } @@ -1893,26 +1877,26 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 370; + this.state = 368; this.match(esql_parser.STATS); - this.state = 372; + this.state = 370; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { case 1: { - this.state = 371; + this.state = 369; localctx._stats = this.aggFields(); } break; } - this.state = 376; + this.state = 374; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { case 1: { - this.state = 374; + this.state = 372; this.match(esql_parser.BY); - this.state = 375; + this.state = 373; localctx._grouping = this.fields(); } break; @@ -1941,25 +1925,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 378; + this.state = 376; this.aggField(); - this.state = 383; + this.state = 381; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 379; + this.state = 377; this.match(esql_parser.COMMA); - this.state = 380; + this.state = 378; this.aggField(); } } } - this.state = 385; + this.state = 383; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 33, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); } } } @@ -1984,16 +1968,16 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 386; + this.state = 384; this.field(); - this.state = 389; + this.state = 387; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 34, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 33, this._ctx) ) { case 1: { - this.state = 387; + this.state = 385; this.match(esql_parser.WHERE); - this.state = 388; + this.state = 386; this.booleanExpression(0); } break; @@ -2022,25 +2006,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 391; + this.state = 389; this.identifierOrParameter(); - this.state = 396; + this.state = 394; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 392; + this.state = 390; this.match(esql_parser.DOT); - this.state = 393; + this.state = 391; this.identifierOrParameter(); } } } - this.state = 398; + this.state = 396; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); } } } @@ -2066,25 +2050,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 399; + this.state = 397; this.identifierPattern(); - this.state = 404; + this.state = 402; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 400; + this.state = 398; this.match(esql_parser.DOT); - this.state = 401; + this.state = 399; this.identifierPattern(); } } } - this.state = 406; + this.state = 404; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); } } } @@ -2110,25 +2094,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 407; + this.state = 405; this.qualifiedNamePattern(); - this.state = 412; + this.state = 410; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 408; + this.state = 406; this.match(esql_parser.COMMA); - this.state = 409; + this.state = 407; this.qualifiedNamePattern(); } } } - this.state = 414; + this.state = 412; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); } } } @@ -2154,7 +2138,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 415; + this.state = 413; _la = this._input.LA(1); if(!(_la===67 || _la===68)) { this._errHandler.recoverInline(this); @@ -2184,24 +2168,24 @@ export default class esql_parser extends parser_config { let localctx: IdentifierPatternContext = new IdentifierPatternContext(this, this._ctx, this.state); this.enterRule(localctx, 66, esql_parser.RULE_identifierPattern); try { - this.state = 420; + this.state = 418; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 38, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 37, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 417; + this.state = 415; this.match(esql_parser.ID_PATTERN); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 418; + this.state = 416; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 419; + this.state = 417; this.parameter(); } break; @@ -2227,14 +2211,14 @@ export default class esql_parser extends parser_config { this.enterRule(localctx, 68, esql_parser.RULE_constant); let _la: number; try { - this.state = 464; + this.state = 462; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 42, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 41, this._ctx) ) { case 1: localctx = new NullLiteralContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 422; + this.state = 420; this.match(esql_parser.NULL); } break; @@ -2242,9 +2226,9 @@ export default class esql_parser extends parser_config { localctx = new QualifiedIntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 423; + this.state = 421; this.integerValue(); - this.state = 424; + this.state = 422; this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; @@ -2252,7 +2236,7 @@ export default class esql_parser extends parser_config { localctx = new DecimalLiteralContext(this, localctx); this.enterOuterAlt(localctx, 3); { - this.state = 426; + this.state = 424; this.decimalValue(); } break; @@ -2260,7 +2244,7 @@ export default class esql_parser extends parser_config { localctx = new IntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 4); { - this.state = 427; + this.state = 425; this.integerValue(); } break; @@ -2268,7 +2252,7 @@ export default class esql_parser extends parser_config { localctx = new BooleanLiteralContext(this, localctx); this.enterOuterAlt(localctx, 5); { - this.state = 428; + this.state = 426; this.booleanValue(); } break; @@ -2276,7 +2260,7 @@ export default class esql_parser extends parser_config { localctx = new InputParameterContext(this, localctx); this.enterOuterAlt(localctx, 6); { - this.state = 429; + this.state = 427; this.parameter(); } break; @@ -2284,7 +2268,7 @@ export default class esql_parser extends parser_config { localctx = new StringLiteralContext(this, localctx); this.enterOuterAlt(localctx, 7); { - this.state = 430; + this.state = 428; this.string_(); } break; @@ -2292,27 +2276,27 @@ export default class esql_parser extends parser_config { localctx = new NumericArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 8); { - this.state = 431; + this.state = 429; this.match(esql_parser.OPENING_BRACKET); - this.state = 432; + this.state = 430; this.numericValue(); - this.state = 437; + this.state = 435; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===33) { + while (_la===34) { { { - this.state = 433; + this.state = 431; this.match(esql_parser.COMMA); - this.state = 434; + this.state = 432; this.numericValue(); } } - this.state = 439; + this.state = 437; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 440; + this.state = 438; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2320,27 +2304,27 @@ export default class esql_parser extends parser_config { localctx = new BooleanArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 9); { - this.state = 442; + this.state = 440; this.match(esql_parser.OPENING_BRACKET); - this.state = 443; + this.state = 441; this.booleanValue(); - this.state = 448; + this.state = 446; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===33) { + while (_la===34) { { { - this.state = 444; + this.state = 442; this.match(esql_parser.COMMA); - this.state = 445; + this.state = 443; this.booleanValue(); } } - this.state = 450; + this.state = 448; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 451; + this.state = 449; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2348,27 +2332,27 @@ export default class esql_parser extends parser_config { localctx = new StringArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 10); { - this.state = 453; + this.state = 451; this.match(esql_parser.OPENING_BRACKET); - this.state = 454; + this.state = 452; this.string_(); - this.state = 459; + this.state = 457; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===33) { + while (_la===34) { { { - this.state = 455; + this.state = 453; this.match(esql_parser.COMMA); - this.state = 456; + this.state = 454; this.string_(); } } - this.state = 461; + this.state = 459; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 462; + this.state = 460; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2393,14 +2377,14 @@ export default class esql_parser extends parser_config { let localctx: ParameterContext = new ParameterContext(this, this._ctx, this.state); this.enterRule(localctx, 70, esql_parser.RULE_parameter); try { - this.state = 468; + this.state = 466; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 47: + case 48: localctx = new InputParamContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 466; + this.state = 464; this.match(esql_parser.PARAM); } break; @@ -2408,7 +2392,7 @@ export default class esql_parser extends parser_config { localctx = new InputNamedOrPositionalParamContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 467; + this.state = 465; this.match(esql_parser.NAMED_OR_POSITIONAL_PARAM); } break; @@ -2435,24 +2419,24 @@ export default class esql_parser extends parser_config { let localctx: IdentifierOrParameterContext = new IdentifierOrParameterContext(this, this._ctx, this.state); this.enterRule(localctx, 72, esql_parser.RULE_identifierOrParameter); try { - this.state = 473; + this.state = 471; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 44, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 470; + this.state = 468; this.identifier(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 471; + this.state = 469; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 472; + this.state = 470; this.parameter(); } break; @@ -2479,9 +2463,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 475; + this.state = 473; this.match(esql_parser.LIMIT); - this.state = 476; + this.state = 474; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2507,27 +2491,27 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 478; + this.state = 476; this.match(esql_parser.SORT); - this.state = 479; + this.state = 477; this.orderExpression(); - this.state = 484; + this.state = 482; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 480; + this.state = 478; this.match(esql_parser.COMMA); - this.state = 481; + this.state = 479; this.orderExpression(); } } } - this.state = 486; + this.state = 484; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 45, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); } } } @@ -2553,17 +2537,17 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 487; + this.state = 485; this.booleanExpression(0); - this.state = 489; + this.state = 487; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 45, this._ctx) ) { case 1: { - this.state = 488; + this.state = 486; localctx._ordering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===30 || _la===34)) { + if(!(_la===31 || _la===35)) { localctx._ordering = this._errHandler.recoverInline(this); } else { @@ -2573,17 +2557,17 @@ export default class esql_parser extends parser_config { } break; } - this.state = 493; + this.state = 491; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 47, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { case 1: { - this.state = 491; + this.state = 489; this.match(esql_parser.NULLS); - this.state = 492; + this.state = 490; localctx._nullOrdering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===37 || _la===40)) { + if(!(_la===38 || _la===41)) { localctx._nullOrdering = this._errHandler.recoverInline(this); } else { @@ -2616,9 +2600,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 495; + this.state = 493; this.match(esql_parser.KEEP); - this.state = 496; + this.state = 494; this.qualifiedNamePatterns(); } } @@ -2643,9 +2627,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 498; + this.state = 496; this.match(esql_parser.DROP); - this.state = 499; + this.state = 497; this.qualifiedNamePatterns(); } } @@ -2671,27 +2655,27 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 501; + this.state = 499; this.match(esql_parser.RENAME); - this.state = 502; + this.state = 500; this.renameClause(); - this.state = 507; + this.state = 505; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 48, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 503; + this.state = 501; this.match(esql_parser.COMMA); - this.state = 504; + this.state = 502; this.renameClause(); } } } - this.state = 509; + this.state = 507; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 48, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); } } } @@ -2716,11 +2700,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 510; + this.state = 508; localctx._oldName = this.qualifiedNamePattern(); - this.state = 511; + this.state = 509; this.match(esql_parser.AS); - this.state = 512; + this.state = 510; localctx._newName = this.qualifiedNamePattern(); } } @@ -2745,18 +2729,18 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 514; + this.state = 512; this.match(esql_parser.DISSECT); - this.state = 515; + this.state = 513; this.primaryExpression(0); - this.state = 516; + this.state = 514; this.string_(); - this.state = 518; + this.state = 516; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 49, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 48, this._ctx) ) { case 1: { - this.state = 517; + this.state = 515; this.commandOptions(); } break; @@ -2784,11 +2768,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 520; + this.state = 518; this.match(esql_parser.GROK); - this.state = 521; + this.state = 519; this.primaryExpression(0); - this.state = 522; + this.state = 520; this.string_(); } } @@ -2813,9 +2797,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 524; + this.state = 522; this.match(esql_parser.MV_EXPAND); - this.state = 525; + this.state = 523; this.qualifiedName(); } } @@ -2841,25 +2825,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 527; + this.state = 525; this.commandOption(); - this.state = 532; + this.state = 530; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 528; + this.state = 526; this.match(esql_parser.COMMA); - this.state = 529; + this.state = 527; this.commandOption(); } } } - this.state = 534; + this.state = 532; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 50, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); } } } @@ -2884,11 +2868,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 535; + this.state = 533; this.identifier(); - this.state = 536; + this.state = 534; this.match(esql_parser.ASSIGN); - this.state = 537; + this.state = 535; this.constant(); } } @@ -2914,9 +2898,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 539; + this.state = 537; _la = this._input.LA(1); - if(!(_la===36 || _la===50)) { + if(!(_la===37 || _la===51)) { this._errHandler.recoverInline(this); } else { @@ -2944,20 +2928,20 @@ export default class esql_parser extends parser_config { let localctx: NumericValueContext = new NumericValueContext(this, this._ctx, this.state); this.enterRule(localctx, 100, esql_parser.RULE_numericValue); try { - this.state = 543; + this.state = 541; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 51, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 50, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 541; + this.state = 539; this.decimalValue(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 542; + this.state = 540; this.integerValue(); } break; @@ -2985,14 +2969,14 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 546; + this.state = 544; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===58 || _la===59) { + if (_la===59 || _la===60) { { - this.state = 545; + this.state = 543; _la = this._input.LA(1); - if(!(_la===58 || _la===59)) { + if(!(_la===59 || _la===60)) { this._errHandler.recoverInline(this); } else { @@ -3002,7 +2986,7 @@ export default class esql_parser extends parser_config { } } - this.state = 548; + this.state = 546; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -3028,14 +3012,14 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 551; + this.state = 549; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===58 || _la===59) { + if (_la===59 || _la===60) { { - this.state = 550; + this.state = 548; _la = this._input.LA(1); - if(!(_la===58 || _la===59)) { + if(!(_la===59 || _la===60)) { this._errHandler.recoverInline(this); } else { @@ -3045,7 +3029,7 @@ export default class esql_parser extends parser_config { } } - this.state = 553; + this.state = 551; this.match(esql_parser.INTEGER_LITERAL); } } @@ -3070,7 +3054,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 555; + this.state = 553; this.match(esql_parser.QUOTED_STRING); } } @@ -3096,9 +3080,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 557; + this.state = 555; _la = this._input.LA(1); - if(!(((((_la - 51)) & ~0x1F) === 0 && ((1 << (_la - 51)) & 125) !== 0))) { + if(!(((((_la - 52)) & ~0x1F) === 0 && ((1 << (_la - 52)) & 125) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -3128,9 +3112,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 559; + this.state = 557; this.match(esql_parser.EXPLAIN); - this.state = 560; + this.state = 558; this.subqueryExpression(); } } @@ -3155,11 +3139,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 562; + this.state = 560; this.match(esql_parser.OPENING_BRACKET); - this.state = 563; + this.state = 561; this.query(0); - this.state = 564; + this.state = 562; this.match(esql_parser.CLOSING_BRACKET); } } @@ -3185,9 +3169,9 @@ export default class esql_parser extends parser_config { localctx = new ShowInfoContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 566; + this.state = 564; this.match(esql_parser.SHOW); - this.state = 567; + this.state = 565; this.match(esql_parser.INFO); } } @@ -3213,48 +3197,48 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 569; + this.state = 567; this.match(esql_parser.ENRICH); - this.state = 570; + this.state = 568; localctx._policyName = this.match(esql_parser.ENRICH_POLICY_NAME); - this.state = 573; + this.state = 571; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 54, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 53, this._ctx) ) { case 1: { - this.state = 571; + this.state = 569; this.match(esql_parser.ON); - this.state = 572; + this.state = 570; localctx._matchField = this.qualifiedNamePattern(); } break; } - this.state = 584; + this.state = 582; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 56, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 55, this._ctx) ) { case 1: { - this.state = 575; + this.state = 573; this.match(esql_parser.WITH); - this.state = 576; + this.state = 574; this.enrichWithClause(); - this.state = 581; + this.state = 579; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 55, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 54, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 577; + this.state = 575; this.match(esql_parser.COMMA); - this.state = 578; + this.state = 576; this.enrichWithClause(); } } } - this.state = 583; + this.state = 581; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 55, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 54, this._ctx); } } break; @@ -3282,19 +3266,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 589; + this.state = 587; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 57, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 56, this._ctx) ) { case 1: { - this.state = 586; + this.state = 584; localctx._newName = this.qualifiedNamePattern(); - this.state = 587; + this.state = 585; this.match(esql_parser.ASSIGN); } break; } - this.state = 591; + this.state = 589; localctx._enrichField = this.qualifiedNamePattern(); } } @@ -3319,13 +3303,13 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 593; + this.state = 591; this.match(esql_parser.DEV_LOOKUP); - this.state = 594; + this.state = 592; localctx._tableName = this.indexPattern(); - this.state = 595; + this.state = 593; this.match(esql_parser.ON); - this.state = 596; + this.state = 594; localctx._matchFields = this.qualifiedNamePatterns(); } } @@ -3350,18 +3334,18 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 598; + this.state = 596; this.match(esql_parser.DEV_INLINESTATS); - this.state = 599; + this.state = 597; localctx._stats = this.aggFields(); - this.state = 602; + this.state = 600; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 58, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 57, this._ctx) ) { case 1: { - this.state = 600; + this.state = 598; this.match(esql_parser.BY); - this.state = 601; + this.state = 599; localctx._grouping = this.fields(); } break; @@ -3469,7 +3453,7 @@ export default class esql_parser extends parser_config { return true; } - public static readonly _serializedATN: number[] = [4,1,120,605,2,0,7,0, + public static readonly _serializedATN: number[] = [4,1,119,603,2,0,7,0, 2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9, 2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2, 17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24, @@ -3489,184 +3473,183 @@ export default class esql_parser extends parser_config { 9,1,9,5,9,250,8,9,10,9,12,9,253,9,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10, 1,10,3,10,263,8,10,1,10,1,10,1,10,5,10,268,8,10,10,10,12,10,271,9,10,1, 11,1,11,1,11,1,11,1,11,1,11,5,11,279,8,11,10,11,12,11,282,9,11,3,11,284, - 8,11,1,11,1,11,1,12,1,12,3,12,290,8,12,1,13,1,13,1,14,1,14,1,14,1,15,1, - 15,1,15,5,15,300,8,15,10,15,12,15,303,9,15,1,16,1,16,1,16,3,16,308,8,16, - 1,16,1,16,1,17,1,17,1,17,1,17,5,17,316,8,17,10,17,12,17,319,9,17,1,17,3, - 17,322,8,17,1,18,1,18,1,18,3,18,327,8,18,1,18,1,18,1,19,1,19,1,20,1,20, - 1,21,1,21,3,21,337,8,21,1,22,1,22,1,22,1,22,5,22,343,8,22,10,22,12,22,346, - 9,22,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,5,24,356,8,24,10,24,12,24, - 359,9,24,1,24,3,24,362,8,24,1,24,1,24,3,24,366,8,24,1,25,1,25,1,25,1,26, - 1,26,3,26,373,8,26,1,26,1,26,3,26,377,8,26,1,27,1,27,1,27,5,27,382,8,27, - 10,27,12,27,385,9,27,1,28,1,28,1,28,3,28,390,8,28,1,29,1,29,1,29,5,29,395, - 8,29,10,29,12,29,398,9,29,1,30,1,30,1,30,5,30,403,8,30,10,30,12,30,406, - 9,30,1,31,1,31,1,31,5,31,411,8,31,10,31,12,31,414,9,31,1,32,1,32,1,33,1, - 33,1,33,3,33,421,8,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34, - 1,34,1,34,1,34,5,34,436,8,34,10,34,12,34,439,9,34,1,34,1,34,1,34,1,34,1, - 34,1,34,5,34,447,8,34,10,34,12,34,450,9,34,1,34,1,34,1,34,1,34,1,34,1,34, - 5,34,458,8,34,10,34,12,34,461,9,34,1,34,1,34,3,34,465,8,34,1,35,1,35,3, - 35,469,8,35,1,36,1,36,1,36,3,36,474,8,36,1,37,1,37,1,37,1,38,1,38,1,38, - 1,38,5,38,483,8,38,10,38,12,38,486,9,38,1,39,1,39,3,39,490,8,39,1,39,1, - 39,3,39,494,8,39,1,40,1,40,1,40,1,41,1,41,1,41,1,42,1,42,1,42,1,42,5,42, - 506,8,42,10,42,12,42,509,9,42,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,3, - 44,519,8,44,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,47,1,47,1,47,5,47,531, - 8,47,10,47,12,47,534,9,47,1,48,1,48,1,48,1,48,1,49,1,49,1,50,1,50,3,50, - 544,8,50,1,51,3,51,547,8,51,1,51,1,51,1,52,3,52,552,8,52,1,52,1,52,1,53, - 1,53,1,54,1,54,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1, - 58,1,58,1,58,3,58,574,8,58,1,58,1,58,1,58,1,58,5,58,580,8,58,10,58,12,58, - 583,9,58,3,58,585,8,58,1,59,1,59,1,59,3,59,590,8,59,1,59,1,59,1,60,1,60, - 1,60,1,60,1,60,1,61,1,61,1,61,1,61,3,61,603,8,61,1,61,0,4,2,10,18,20,62, - 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50, - 52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98, - 100,102,104,106,108,110,112,114,116,118,120,122,0,8,1,0,58,59,1,0,60,62, - 2,0,25,25,76,76,1,0,67,68,2,0,30,30,34,34,2,0,37,37,40,40,2,0,36,36,50, - 50,2,0,51,51,53,57,631,0,124,1,0,0,0,2,127,1,0,0,0,4,144,1,0,0,0,6,162, - 1,0,0,0,8,164,1,0,0,0,10,197,1,0,0,0,12,224,1,0,0,0,14,226,1,0,0,0,16,235, - 1,0,0,0,18,241,1,0,0,0,20,262,1,0,0,0,22,272,1,0,0,0,24,289,1,0,0,0,26, - 291,1,0,0,0,28,293,1,0,0,0,30,296,1,0,0,0,32,307,1,0,0,0,34,311,1,0,0,0, - 36,326,1,0,0,0,38,330,1,0,0,0,40,332,1,0,0,0,42,336,1,0,0,0,44,338,1,0, - 0,0,46,347,1,0,0,0,48,351,1,0,0,0,50,367,1,0,0,0,52,370,1,0,0,0,54,378, - 1,0,0,0,56,386,1,0,0,0,58,391,1,0,0,0,60,399,1,0,0,0,62,407,1,0,0,0,64, - 415,1,0,0,0,66,420,1,0,0,0,68,464,1,0,0,0,70,468,1,0,0,0,72,473,1,0,0,0, - 74,475,1,0,0,0,76,478,1,0,0,0,78,487,1,0,0,0,80,495,1,0,0,0,82,498,1,0, - 0,0,84,501,1,0,0,0,86,510,1,0,0,0,88,514,1,0,0,0,90,520,1,0,0,0,92,524, - 1,0,0,0,94,527,1,0,0,0,96,535,1,0,0,0,98,539,1,0,0,0,100,543,1,0,0,0,102, - 546,1,0,0,0,104,551,1,0,0,0,106,555,1,0,0,0,108,557,1,0,0,0,110,559,1,0, - 0,0,112,562,1,0,0,0,114,566,1,0,0,0,116,569,1,0,0,0,118,589,1,0,0,0,120, - 593,1,0,0,0,122,598,1,0,0,0,124,125,3,2,1,0,125,126,5,0,0,1,126,1,1,0,0, - 0,127,128,6,1,-1,0,128,129,3,4,2,0,129,135,1,0,0,0,130,131,10,1,0,0,131, - 132,5,24,0,0,132,134,3,6,3,0,133,130,1,0,0,0,134,137,1,0,0,0,135,133,1, - 0,0,0,135,136,1,0,0,0,136,3,1,0,0,0,137,135,1,0,0,0,138,145,3,110,55,0, - 139,145,3,34,17,0,140,145,3,28,14,0,141,145,3,114,57,0,142,143,4,2,1,0, - 143,145,3,48,24,0,144,138,1,0,0,0,144,139,1,0,0,0,144,140,1,0,0,0,144,141, - 1,0,0,0,144,142,1,0,0,0,145,5,1,0,0,0,146,163,3,50,25,0,147,163,3,8,4,0, - 148,163,3,80,40,0,149,163,3,74,37,0,150,163,3,52,26,0,151,163,3,76,38,0, - 152,163,3,82,41,0,153,163,3,84,42,0,154,163,3,88,44,0,155,163,3,90,45,0, - 156,163,3,116,58,0,157,163,3,92,46,0,158,159,4,3,2,0,159,163,3,122,61,0, - 160,161,4,3,3,0,161,163,3,120,60,0,162,146,1,0,0,0,162,147,1,0,0,0,162, - 148,1,0,0,0,162,149,1,0,0,0,162,150,1,0,0,0,162,151,1,0,0,0,162,152,1,0, - 0,0,162,153,1,0,0,0,162,154,1,0,0,0,162,155,1,0,0,0,162,156,1,0,0,0,162, - 157,1,0,0,0,162,158,1,0,0,0,162,160,1,0,0,0,163,7,1,0,0,0,164,165,5,16, - 0,0,165,166,3,10,5,0,166,9,1,0,0,0,167,168,6,5,-1,0,168,169,5,43,0,0,169, - 198,3,10,5,8,170,198,3,16,8,0,171,198,3,12,6,0,172,174,3,16,8,0,173,175, - 5,43,0,0,174,173,1,0,0,0,174,175,1,0,0,0,175,176,1,0,0,0,176,177,5,38,0, - 0,177,178,5,42,0,0,178,183,3,16,8,0,179,180,5,33,0,0,180,182,3,16,8,0,181, - 179,1,0,0,0,182,185,1,0,0,0,183,181,1,0,0,0,183,184,1,0,0,0,184,186,1,0, - 0,0,185,183,1,0,0,0,186,187,5,49,0,0,187,198,1,0,0,0,188,189,3,16,8,0,189, - 191,5,39,0,0,190,192,5,43,0,0,191,190,1,0,0,0,191,192,1,0,0,0,192,193,1, - 0,0,0,193,194,5,44,0,0,194,198,1,0,0,0,195,196,4,5,4,0,196,198,3,14,7,0, - 197,167,1,0,0,0,197,170,1,0,0,0,197,171,1,0,0,0,197,172,1,0,0,0,197,188, - 1,0,0,0,197,195,1,0,0,0,198,207,1,0,0,0,199,200,10,5,0,0,200,201,5,29,0, - 0,201,206,3,10,5,6,202,203,10,4,0,0,203,204,5,46,0,0,204,206,3,10,5,5,205, - 199,1,0,0,0,205,202,1,0,0,0,206,209,1,0,0,0,207,205,1,0,0,0,207,208,1,0, - 0,0,208,11,1,0,0,0,209,207,1,0,0,0,210,212,3,16,8,0,211,213,5,43,0,0,212, - 211,1,0,0,0,212,213,1,0,0,0,213,214,1,0,0,0,214,215,5,41,0,0,215,216,3, - 106,53,0,216,225,1,0,0,0,217,219,3,16,8,0,218,220,5,43,0,0,219,218,1,0, - 0,0,219,220,1,0,0,0,220,221,1,0,0,0,221,222,5,48,0,0,222,223,3,106,53,0, - 223,225,1,0,0,0,224,210,1,0,0,0,224,217,1,0,0,0,225,13,1,0,0,0,226,227, - 3,16,8,0,227,228,5,63,0,0,228,229,3,106,53,0,229,15,1,0,0,0,230,236,3,18, - 9,0,231,232,3,18,9,0,232,233,3,108,54,0,233,234,3,18,9,0,234,236,1,0,0, - 0,235,230,1,0,0,0,235,231,1,0,0,0,236,17,1,0,0,0,237,238,6,9,-1,0,238,242, - 3,20,10,0,239,240,7,0,0,0,240,242,3,18,9,3,241,237,1,0,0,0,241,239,1,0, - 0,0,242,251,1,0,0,0,243,244,10,2,0,0,244,245,7,1,0,0,245,250,3,18,9,3,246, - 247,10,1,0,0,247,248,7,0,0,0,248,250,3,18,9,2,249,243,1,0,0,0,249,246,1, - 0,0,0,250,253,1,0,0,0,251,249,1,0,0,0,251,252,1,0,0,0,252,19,1,0,0,0,253, - 251,1,0,0,0,254,255,6,10,-1,0,255,263,3,68,34,0,256,263,3,58,29,0,257,263, - 3,22,11,0,258,259,5,42,0,0,259,260,3,10,5,0,260,261,5,49,0,0,261,263,1, - 0,0,0,262,254,1,0,0,0,262,256,1,0,0,0,262,257,1,0,0,0,262,258,1,0,0,0,263, - 269,1,0,0,0,264,265,10,1,0,0,265,266,5,32,0,0,266,268,3,26,13,0,267,264, - 1,0,0,0,268,271,1,0,0,0,269,267,1,0,0,0,269,270,1,0,0,0,270,21,1,0,0,0, - 271,269,1,0,0,0,272,273,3,24,12,0,273,283,5,42,0,0,274,284,5,60,0,0,275, - 280,3,10,5,0,276,277,5,33,0,0,277,279,3,10,5,0,278,276,1,0,0,0,279,282, - 1,0,0,0,280,278,1,0,0,0,280,281,1,0,0,0,281,284,1,0,0,0,282,280,1,0,0,0, - 283,274,1,0,0,0,283,275,1,0,0,0,283,284,1,0,0,0,284,285,1,0,0,0,285,286, - 5,49,0,0,286,23,1,0,0,0,287,290,5,63,0,0,288,290,3,72,36,0,289,287,1,0, - 0,0,289,288,1,0,0,0,290,25,1,0,0,0,291,292,3,64,32,0,292,27,1,0,0,0,293, - 294,5,12,0,0,294,295,3,30,15,0,295,29,1,0,0,0,296,301,3,32,16,0,297,298, - 5,33,0,0,298,300,3,32,16,0,299,297,1,0,0,0,300,303,1,0,0,0,301,299,1,0, - 0,0,301,302,1,0,0,0,302,31,1,0,0,0,303,301,1,0,0,0,304,305,3,58,29,0,305, - 306,5,31,0,0,306,308,1,0,0,0,307,304,1,0,0,0,307,308,1,0,0,0,308,309,1, - 0,0,0,309,310,3,10,5,0,310,33,1,0,0,0,311,312,5,6,0,0,312,317,3,36,18,0, - 313,314,5,33,0,0,314,316,3,36,18,0,315,313,1,0,0,0,316,319,1,0,0,0,317, - 315,1,0,0,0,317,318,1,0,0,0,318,321,1,0,0,0,319,317,1,0,0,0,320,322,3,42, - 21,0,321,320,1,0,0,0,321,322,1,0,0,0,322,35,1,0,0,0,323,324,3,38,19,0,324, - 325,5,104,0,0,325,327,1,0,0,0,326,323,1,0,0,0,326,327,1,0,0,0,327,328,1, - 0,0,0,328,329,3,40,20,0,329,37,1,0,0,0,330,331,5,76,0,0,331,39,1,0,0,0, - 332,333,7,2,0,0,333,41,1,0,0,0,334,337,3,44,22,0,335,337,3,46,23,0,336, - 334,1,0,0,0,336,335,1,0,0,0,337,43,1,0,0,0,338,339,5,75,0,0,339,344,5,76, - 0,0,340,341,5,33,0,0,341,343,5,76,0,0,342,340,1,0,0,0,343,346,1,0,0,0,344, - 342,1,0,0,0,344,345,1,0,0,0,345,45,1,0,0,0,346,344,1,0,0,0,347,348,5,65, - 0,0,348,349,3,44,22,0,349,350,5,66,0,0,350,47,1,0,0,0,351,352,5,19,0,0, - 352,357,3,36,18,0,353,354,5,33,0,0,354,356,3,36,18,0,355,353,1,0,0,0,356, - 359,1,0,0,0,357,355,1,0,0,0,357,358,1,0,0,0,358,361,1,0,0,0,359,357,1,0, - 0,0,360,362,3,54,27,0,361,360,1,0,0,0,361,362,1,0,0,0,362,365,1,0,0,0,363, - 364,5,28,0,0,364,366,3,30,15,0,365,363,1,0,0,0,365,366,1,0,0,0,366,49,1, - 0,0,0,367,368,5,4,0,0,368,369,3,30,15,0,369,51,1,0,0,0,370,372,5,15,0,0, - 371,373,3,54,27,0,372,371,1,0,0,0,372,373,1,0,0,0,373,376,1,0,0,0,374,375, - 5,28,0,0,375,377,3,30,15,0,376,374,1,0,0,0,376,377,1,0,0,0,377,53,1,0,0, - 0,378,383,3,56,28,0,379,380,5,33,0,0,380,382,3,56,28,0,381,379,1,0,0,0, - 382,385,1,0,0,0,383,381,1,0,0,0,383,384,1,0,0,0,384,55,1,0,0,0,385,383, - 1,0,0,0,386,389,3,32,16,0,387,388,5,16,0,0,388,390,3,10,5,0,389,387,1,0, - 0,0,389,390,1,0,0,0,390,57,1,0,0,0,391,396,3,72,36,0,392,393,5,35,0,0,393, - 395,3,72,36,0,394,392,1,0,0,0,395,398,1,0,0,0,396,394,1,0,0,0,396,397,1, - 0,0,0,397,59,1,0,0,0,398,396,1,0,0,0,399,404,3,66,33,0,400,401,5,35,0,0, - 401,403,3,66,33,0,402,400,1,0,0,0,403,406,1,0,0,0,404,402,1,0,0,0,404,405, - 1,0,0,0,405,61,1,0,0,0,406,404,1,0,0,0,407,412,3,60,30,0,408,409,5,33,0, - 0,409,411,3,60,30,0,410,408,1,0,0,0,411,414,1,0,0,0,412,410,1,0,0,0,412, - 413,1,0,0,0,413,63,1,0,0,0,414,412,1,0,0,0,415,416,7,3,0,0,416,65,1,0,0, - 0,417,421,5,80,0,0,418,419,4,33,10,0,419,421,3,70,35,0,420,417,1,0,0,0, - 420,418,1,0,0,0,421,67,1,0,0,0,422,465,5,44,0,0,423,424,3,104,52,0,424, - 425,5,67,0,0,425,465,1,0,0,0,426,465,3,102,51,0,427,465,3,104,52,0,428, - 465,3,98,49,0,429,465,3,70,35,0,430,465,3,106,53,0,431,432,5,65,0,0,432, - 437,3,100,50,0,433,434,5,33,0,0,434,436,3,100,50,0,435,433,1,0,0,0,436, - 439,1,0,0,0,437,435,1,0,0,0,437,438,1,0,0,0,438,440,1,0,0,0,439,437,1,0, - 0,0,440,441,5,66,0,0,441,465,1,0,0,0,442,443,5,65,0,0,443,448,3,98,49,0, - 444,445,5,33,0,0,445,447,3,98,49,0,446,444,1,0,0,0,447,450,1,0,0,0,448, - 446,1,0,0,0,448,449,1,0,0,0,449,451,1,0,0,0,450,448,1,0,0,0,451,452,5,66, - 0,0,452,465,1,0,0,0,453,454,5,65,0,0,454,459,3,106,53,0,455,456,5,33,0, - 0,456,458,3,106,53,0,457,455,1,0,0,0,458,461,1,0,0,0,459,457,1,0,0,0,459, - 460,1,0,0,0,460,462,1,0,0,0,461,459,1,0,0,0,462,463,5,66,0,0,463,465,1, - 0,0,0,464,422,1,0,0,0,464,423,1,0,0,0,464,426,1,0,0,0,464,427,1,0,0,0,464, - 428,1,0,0,0,464,429,1,0,0,0,464,430,1,0,0,0,464,431,1,0,0,0,464,442,1,0, - 0,0,464,453,1,0,0,0,465,69,1,0,0,0,466,469,5,47,0,0,467,469,5,64,0,0,468, - 466,1,0,0,0,468,467,1,0,0,0,469,71,1,0,0,0,470,474,3,64,32,0,471,472,4, - 36,11,0,472,474,3,70,35,0,473,470,1,0,0,0,473,471,1,0,0,0,474,73,1,0,0, - 0,475,476,5,9,0,0,476,477,5,26,0,0,477,75,1,0,0,0,478,479,5,14,0,0,479, - 484,3,78,39,0,480,481,5,33,0,0,481,483,3,78,39,0,482,480,1,0,0,0,483,486, - 1,0,0,0,484,482,1,0,0,0,484,485,1,0,0,0,485,77,1,0,0,0,486,484,1,0,0,0, - 487,489,3,10,5,0,488,490,7,4,0,0,489,488,1,0,0,0,489,490,1,0,0,0,490,493, - 1,0,0,0,491,492,5,45,0,0,492,494,7,5,0,0,493,491,1,0,0,0,493,494,1,0,0, - 0,494,79,1,0,0,0,495,496,5,8,0,0,496,497,3,62,31,0,497,81,1,0,0,0,498,499, - 5,2,0,0,499,500,3,62,31,0,500,83,1,0,0,0,501,502,5,11,0,0,502,507,3,86, - 43,0,503,504,5,33,0,0,504,506,3,86,43,0,505,503,1,0,0,0,506,509,1,0,0,0, - 507,505,1,0,0,0,507,508,1,0,0,0,508,85,1,0,0,0,509,507,1,0,0,0,510,511, - 3,60,30,0,511,512,5,84,0,0,512,513,3,60,30,0,513,87,1,0,0,0,514,515,5,1, - 0,0,515,516,3,20,10,0,516,518,3,106,53,0,517,519,3,94,47,0,518,517,1,0, - 0,0,518,519,1,0,0,0,519,89,1,0,0,0,520,521,5,7,0,0,521,522,3,20,10,0,522, - 523,3,106,53,0,523,91,1,0,0,0,524,525,5,10,0,0,525,526,3,58,29,0,526,93, - 1,0,0,0,527,532,3,96,48,0,528,529,5,33,0,0,529,531,3,96,48,0,530,528,1, - 0,0,0,531,534,1,0,0,0,532,530,1,0,0,0,532,533,1,0,0,0,533,95,1,0,0,0,534, - 532,1,0,0,0,535,536,3,64,32,0,536,537,5,31,0,0,537,538,3,68,34,0,538,97, - 1,0,0,0,539,540,7,6,0,0,540,99,1,0,0,0,541,544,3,102,51,0,542,544,3,104, - 52,0,543,541,1,0,0,0,543,542,1,0,0,0,544,101,1,0,0,0,545,547,7,0,0,0,546, - 545,1,0,0,0,546,547,1,0,0,0,547,548,1,0,0,0,548,549,5,27,0,0,549,103,1, - 0,0,0,550,552,7,0,0,0,551,550,1,0,0,0,551,552,1,0,0,0,552,553,1,0,0,0,553, - 554,5,26,0,0,554,105,1,0,0,0,555,556,5,25,0,0,556,107,1,0,0,0,557,558,7, - 7,0,0,558,109,1,0,0,0,559,560,5,5,0,0,560,561,3,112,56,0,561,111,1,0,0, - 0,562,563,5,65,0,0,563,564,3,2,1,0,564,565,5,66,0,0,565,113,1,0,0,0,566, - 567,5,13,0,0,567,568,5,100,0,0,568,115,1,0,0,0,569,570,5,3,0,0,570,573, - 5,90,0,0,571,572,5,88,0,0,572,574,3,60,30,0,573,571,1,0,0,0,573,574,1,0, - 0,0,574,584,1,0,0,0,575,576,5,89,0,0,576,581,3,118,59,0,577,578,5,33,0, - 0,578,580,3,118,59,0,579,577,1,0,0,0,580,583,1,0,0,0,581,579,1,0,0,0,581, - 582,1,0,0,0,582,585,1,0,0,0,583,581,1,0,0,0,584,575,1,0,0,0,584,585,1,0, - 0,0,585,117,1,0,0,0,586,587,3,60,30,0,587,588,5,31,0,0,588,590,1,0,0,0, - 589,586,1,0,0,0,589,590,1,0,0,0,590,591,1,0,0,0,591,592,3,60,30,0,592,119, - 1,0,0,0,593,594,5,18,0,0,594,595,3,36,18,0,595,596,5,88,0,0,596,597,3,62, - 31,0,597,121,1,0,0,0,598,599,5,17,0,0,599,602,3,54,27,0,600,601,5,28,0, - 0,601,603,3,30,15,0,602,600,1,0,0,0,602,603,1,0,0,0,603,123,1,0,0,0,59, + 8,11,1,11,1,11,1,12,1,12,1,13,1,13,1,14,1,14,1,14,1,15,1,15,1,15,5,15,298, + 8,15,10,15,12,15,301,9,15,1,16,1,16,1,16,3,16,306,8,16,1,16,1,16,1,17,1, + 17,1,17,1,17,5,17,314,8,17,10,17,12,17,317,9,17,1,17,3,17,320,8,17,1,18, + 1,18,1,18,3,18,325,8,18,1,18,1,18,1,19,1,19,1,20,1,20,1,21,1,21,3,21,335, + 8,21,1,22,1,22,1,22,1,22,5,22,341,8,22,10,22,12,22,344,9,22,1,23,1,23,1, + 23,1,23,1,24,1,24,1,24,1,24,5,24,354,8,24,10,24,12,24,357,9,24,1,24,3,24, + 360,8,24,1,24,1,24,3,24,364,8,24,1,25,1,25,1,25,1,26,1,26,3,26,371,8,26, + 1,26,1,26,3,26,375,8,26,1,27,1,27,1,27,5,27,380,8,27,10,27,12,27,383,9, + 27,1,28,1,28,1,28,3,28,388,8,28,1,29,1,29,1,29,5,29,393,8,29,10,29,12,29, + 396,9,29,1,30,1,30,1,30,5,30,401,8,30,10,30,12,30,404,9,30,1,31,1,31,1, + 31,5,31,409,8,31,10,31,12,31,412,9,31,1,32,1,32,1,33,1,33,1,33,3,33,419, + 8,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,5, + 34,434,8,34,10,34,12,34,437,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,445, + 8,34,10,34,12,34,448,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,456,8,34,10, + 34,12,34,459,9,34,1,34,1,34,3,34,463,8,34,1,35,1,35,3,35,467,8,35,1,36, + 1,36,1,36,3,36,472,8,36,1,37,1,37,1,37,1,38,1,38,1,38,1,38,5,38,481,8,38, + 10,38,12,38,484,9,38,1,39,1,39,3,39,488,8,39,1,39,1,39,3,39,492,8,39,1, + 40,1,40,1,40,1,41,1,41,1,41,1,42,1,42,1,42,1,42,5,42,504,8,42,10,42,12, + 42,507,9,42,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,3,44,517,8,44,1,45, + 1,45,1,45,1,45,1,46,1,46,1,46,1,47,1,47,1,47,5,47,529,8,47,10,47,12,47, + 532,9,47,1,48,1,48,1,48,1,48,1,49,1,49,1,50,1,50,3,50,542,8,50,1,51,3,51, + 545,8,51,1,51,1,51,1,52,3,52,550,8,52,1,52,1,52,1,53,1,53,1,54,1,54,1,55, + 1,55,1,55,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,58,3,58,572, + 8,58,1,58,1,58,1,58,1,58,5,58,578,8,58,10,58,12,58,581,9,58,3,58,583,8, + 58,1,59,1,59,1,59,3,59,588,8,59,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,61, + 1,61,1,61,1,61,3,61,601,8,61,1,61,0,4,2,10,18,20,62,0,2,4,6,8,10,12,14, + 16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62, + 64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108, + 110,112,114,116,118,120,122,0,8,1,0,59,60,1,0,61,63,2,0,26,26,76,76,1,0, + 67,68,2,0,31,31,35,35,2,0,38,38,41,41,2,0,37,37,51,51,2,0,52,52,54,58,628, + 0,124,1,0,0,0,2,127,1,0,0,0,4,144,1,0,0,0,6,162,1,0,0,0,8,164,1,0,0,0,10, + 197,1,0,0,0,12,224,1,0,0,0,14,226,1,0,0,0,16,235,1,0,0,0,18,241,1,0,0,0, + 20,262,1,0,0,0,22,272,1,0,0,0,24,287,1,0,0,0,26,289,1,0,0,0,28,291,1,0, + 0,0,30,294,1,0,0,0,32,305,1,0,0,0,34,309,1,0,0,0,36,324,1,0,0,0,38,328, + 1,0,0,0,40,330,1,0,0,0,42,334,1,0,0,0,44,336,1,0,0,0,46,345,1,0,0,0,48, + 349,1,0,0,0,50,365,1,0,0,0,52,368,1,0,0,0,54,376,1,0,0,0,56,384,1,0,0,0, + 58,389,1,0,0,0,60,397,1,0,0,0,62,405,1,0,0,0,64,413,1,0,0,0,66,418,1,0, + 0,0,68,462,1,0,0,0,70,466,1,0,0,0,72,471,1,0,0,0,74,473,1,0,0,0,76,476, + 1,0,0,0,78,485,1,0,0,0,80,493,1,0,0,0,82,496,1,0,0,0,84,499,1,0,0,0,86, + 508,1,0,0,0,88,512,1,0,0,0,90,518,1,0,0,0,92,522,1,0,0,0,94,525,1,0,0,0, + 96,533,1,0,0,0,98,537,1,0,0,0,100,541,1,0,0,0,102,544,1,0,0,0,104,549,1, + 0,0,0,106,553,1,0,0,0,108,555,1,0,0,0,110,557,1,0,0,0,112,560,1,0,0,0,114, + 564,1,0,0,0,116,567,1,0,0,0,118,587,1,0,0,0,120,591,1,0,0,0,122,596,1,0, + 0,0,124,125,3,2,1,0,125,126,5,0,0,1,126,1,1,0,0,0,127,128,6,1,-1,0,128, + 129,3,4,2,0,129,135,1,0,0,0,130,131,10,1,0,0,131,132,5,25,0,0,132,134,3, + 6,3,0,133,130,1,0,0,0,134,137,1,0,0,0,135,133,1,0,0,0,135,136,1,0,0,0,136, + 3,1,0,0,0,137,135,1,0,0,0,138,145,3,110,55,0,139,145,3,34,17,0,140,145, + 3,28,14,0,141,145,3,114,57,0,142,143,4,2,1,0,143,145,3,48,24,0,144,138, + 1,0,0,0,144,139,1,0,0,0,144,140,1,0,0,0,144,141,1,0,0,0,144,142,1,0,0,0, + 145,5,1,0,0,0,146,163,3,50,25,0,147,163,3,8,4,0,148,163,3,80,40,0,149,163, + 3,74,37,0,150,163,3,52,26,0,151,163,3,76,38,0,152,163,3,82,41,0,153,163, + 3,84,42,0,154,163,3,88,44,0,155,163,3,90,45,0,156,163,3,116,58,0,157,163, + 3,92,46,0,158,159,4,3,2,0,159,163,3,122,61,0,160,161,4,3,3,0,161,163,3, + 120,60,0,162,146,1,0,0,0,162,147,1,0,0,0,162,148,1,0,0,0,162,149,1,0,0, + 0,162,150,1,0,0,0,162,151,1,0,0,0,162,152,1,0,0,0,162,153,1,0,0,0,162,154, + 1,0,0,0,162,155,1,0,0,0,162,156,1,0,0,0,162,157,1,0,0,0,162,158,1,0,0,0, + 162,160,1,0,0,0,163,7,1,0,0,0,164,165,5,16,0,0,165,166,3,10,5,0,166,9,1, + 0,0,0,167,168,6,5,-1,0,168,169,5,44,0,0,169,198,3,10,5,8,170,198,3,16,8, + 0,171,198,3,12,6,0,172,174,3,16,8,0,173,175,5,44,0,0,174,173,1,0,0,0,174, + 175,1,0,0,0,175,176,1,0,0,0,176,177,5,39,0,0,177,178,5,43,0,0,178,183,3, + 16,8,0,179,180,5,34,0,0,180,182,3,16,8,0,181,179,1,0,0,0,182,185,1,0,0, + 0,183,181,1,0,0,0,183,184,1,0,0,0,184,186,1,0,0,0,185,183,1,0,0,0,186,187, + 5,50,0,0,187,198,1,0,0,0,188,189,3,16,8,0,189,191,5,40,0,0,190,192,5,44, + 0,0,191,190,1,0,0,0,191,192,1,0,0,0,192,193,1,0,0,0,193,194,5,45,0,0,194, + 198,1,0,0,0,195,196,4,5,4,0,196,198,3,14,7,0,197,167,1,0,0,0,197,170,1, + 0,0,0,197,171,1,0,0,0,197,172,1,0,0,0,197,188,1,0,0,0,197,195,1,0,0,0,198, + 207,1,0,0,0,199,200,10,5,0,0,200,201,5,30,0,0,201,206,3,10,5,6,202,203, + 10,4,0,0,203,204,5,47,0,0,204,206,3,10,5,5,205,199,1,0,0,0,205,202,1,0, + 0,0,206,209,1,0,0,0,207,205,1,0,0,0,207,208,1,0,0,0,208,11,1,0,0,0,209, + 207,1,0,0,0,210,212,3,16,8,0,211,213,5,44,0,0,212,211,1,0,0,0,212,213,1, + 0,0,0,213,214,1,0,0,0,214,215,5,42,0,0,215,216,3,106,53,0,216,225,1,0,0, + 0,217,219,3,16,8,0,218,220,5,44,0,0,219,218,1,0,0,0,219,220,1,0,0,0,220, + 221,1,0,0,0,221,222,5,49,0,0,222,223,3,106,53,0,223,225,1,0,0,0,224,210, + 1,0,0,0,224,217,1,0,0,0,225,13,1,0,0,0,226,227,3,58,29,0,227,228,5,24,0, + 0,228,229,3,68,34,0,229,15,1,0,0,0,230,236,3,18,9,0,231,232,3,18,9,0,232, + 233,3,108,54,0,233,234,3,18,9,0,234,236,1,0,0,0,235,230,1,0,0,0,235,231, + 1,0,0,0,236,17,1,0,0,0,237,238,6,9,-1,0,238,242,3,20,10,0,239,240,7,0,0, + 0,240,242,3,18,9,3,241,237,1,0,0,0,241,239,1,0,0,0,242,251,1,0,0,0,243, + 244,10,2,0,0,244,245,7,1,0,0,245,250,3,18,9,3,246,247,10,1,0,0,247,248, + 7,0,0,0,248,250,3,18,9,2,249,243,1,0,0,0,249,246,1,0,0,0,250,253,1,0,0, + 0,251,249,1,0,0,0,251,252,1,0,0,0,252,19,1,0,0,0,253,251,1,0,0,0,254,255, + 6,10,-1,0,255,263,3,68,34,0,256,263,3,58,29,0,257,263,3,22,11,0,258,259, + 5,43,0,0,259,260,3,10,5,0,260,261,5,50,0,0,261,263,1,0,0,0,262,254,1,0, + 0,0,262,256,1,0,0,0,262,257,1,0,0,0,262,258,1,0,0,0,263,269,1,0,0,0,264, + 265,10,1,0,0,265,266,5,33,0,0,266,268,3,26,13,0,267,264,1,0,0,0,268,271, + 1,0,0,0,269,267,1,0,0,0,269,270,1,0,0,0,270,21,1,0,0,0,271,269,1,0,0,0, + 272,273,3,24,12,0,273,283,5,43,0,0,274,284,5,61,0,0,275,280,3,10,5,0,276, + 277,5,34,0,0,277,279,3,10,5,0,278,276,1,0,0,0,279,282,1,0,0,0,280,278,1, + 0,0,0,280,281,1,0,0,0,281,284,1,0,0,0,282,280,1,0,0,0,283,274,1,0,0,0,283, + 275,1,0,0,0,283,284,1,0,0,0,284,285,1,0,0,0,285,286,5,50,0,0,286,23,1,0, + 0,0,287,288,3,72,36,0,288,25,1,0,0,0,289,290,3,64,32,0,290,27,1,0,0,0,291, + 292,5,12,0,0,292,293,3,30,15,0,293,29,1,0,0,0,294,299,3,32,16,0,295,296, + 5,34,0,0,296,298,3,32,16,0,297,295,1,0,0,0,298,301,1,0,0,0,299,297,1,0, + 0,0,299,300,1,0,0,0,300,31,1,0,0,0,301,299,1,0,0,0,302,303,3,58,29,0,303, + 304,5,32,0,0,304,306,1,0,0,0,305,302,1,0,0,0,305,306,1,0,0,0,306,307,1, + 0,0,0,307,308,3,10,5,0,308,33,1,0,0,0,309,310,5,6,0,0,310,315,3,36,18,0, + 311,312,5,34,0,0,312,314,3,36,18,0,313,311,1,0,0,0,314,317,1,0,0,0,315, + 313,1,0,0,0,315,316,1,0,0,0,316,319,1,0,0,0,317,315,1,0,0,0,318,320,3,42, + 21,0,319,318,1,0,0,0,319,320,1,0,0,0,320,35,1,0,0,0,321,322,3,38,19,0,322, + 323,5,24,0,0,323,325,1,0,0,0,324,321,1,0,0,0,324,325,1,0,0,0,325,326,1, + 0,0,0,326,327,3,40,20,0,327,37,1,0,0,0,328,329,5,76,0,0,329,39,1,0,0,0, + 330,331,7,2,0,0,331,41,1,0,0,0,332,335,3,44,22,0,333,335,3,46,23,0,334, + 332,1,0,0,0,334,333,1,0,0,0,335,43,1,0,0,0,336,337,5,75,0,0,337,342,5,76, + 0,0,338,339,5,34,0,0,339,341,5,76,0,0,340,338,1,0,0,0,341,344,1,0,0,0,342, + 340,1,0,0,0,342,343,1,0,0,0,343,45,1,0,0,0,344,342,1,0,0,0,345,346,5,65, + 0,0,346,347,3,44,22,0,347,348,5,66,0,0,348,47,1,0,0,0,349,350,5,19,0,0, + 350,355,3,36,18,0,351,352,5,34,0,0,352,354,3,36,18,0,353,351,1,0,0,0,354, + 357,1,0,0,0,355,353,1,0,0,0,355,356,1,0,0,0,356,359,1,0,0,0,357,355,1,0, + 0,0,358,360,3,54,27,0,359,358,1,0,0,0,359,360,1,0,0,0,360,363,1,0,0,0,361, + 362,5,29,0,0,362,364,3,30,15,0,363,361,1,0,0,0,363,364,1,0,0,0,364,49,1, + 0,0,0,365,366,5,4,0,0,366,367,3,30,15,0,367,51,1,0,0,0,368,370,5,15,0,0, + 369,371,3,54,27,0,370,369,1,0,0,0,370,371,1,0,0,0,371,374,1,0,0,0,372,373, + 5,29,0,0,373,375,3,30,15,0,374,372,1,0,0,0,374,375,1,0,0,0,375,53,1,0,0, + 0,376,381,3,56,28,0,377,378,5,34,0,0,378,380,3,56,28,0,379,377,1,0,0,0, + 380,383,1,0,0,0,381,379,1,0,0,0,381,382,1,0,0,0,382,55,1,0,0,0,383,381, + 1,0,0,0,384,387,3,32,16,0,385,386,5,16,0,0,386,388,3,10,5,0,387,385,1,0, + 0,0,387,388,1,0,0,0,388,57,1,0,0,0,389,394,3,72,36,0,390,391,5,36,0,0,391, + 393,3,72,36,0,392,390,1,0,0,0,393,396,1,0,0,0,394,392,1,0,0,0,394,395,1, + 0,0,0,395,59,1,0,0,0,396,394,1,0,0,0,397,402,3,66,33,0,398,399,5,36,0,0, + 399,401,3,66,33,0,400,398,1,0,0,0,401,404,1,0,0,0,402,400,1,0,0,0,402,403, + 1,0,0,0,403,61,1,0,0,0,404,402,1,0,0,0,405,410,3,60,30,0,406,407,5,34,0, + 0,407,409,3,60,30,0,408,406,1,0,0,0,409,412,1,0,0,0,410,408,1,0,0,0,410, + 411,1,0,0,0,411,63,1,0,0,0,412,410,1,0,0,0,413,414,7,3,0,0,414,65,1,0,0, + 0,415,419,5,80,0,0,416,417,4,33,10,0,417,419,3,70,35,0,418,415,1,0,0,0, + 418,416,1,0,0,0,419,67,1,0,0,0,420,463,5,45,0,0,421,422,3,104,52,0,422, + 423,5,67,0,0,423,463,1,0,0,0,424,463,3,102,51,0,425,463,3,104,52,0,426, + 463,3,98,49,0,427,463,3,70,35,0,428,463,3,106,53,0,429,430,5,65,0,0,430, + 435,3,100,50,0,431,432,5,34,0,0,432,434,3,100,50,0,433,431,1,0,0,0,434, + 437,1,0,0,0,435,433,1,0,0,0,435,436,1,0,0,0,436,438,1,0,0,0,437,435,1,0, + 0,0,438,439,5,66,0,0,439,463,1,0,0,0,440,441,5,65,0,0,441,446,3,98,49,0, + 442,443,5,34,0,0,443,445,3,98,49,0,444,442,1,0,0,0,445,448,1,0,0,0,446, + 444,1,0,0,0,446,447,1,0,0,0,447,449,1,0,0,0,448,446,1,0,0,0,449,450,5,66, + 0,0,450,463,1,0,0,0,451,452,5,65,0,0,452,457,3,106,53,0,453,454,5,34,0, + 0,454,456,3,106,53,0,455,453,1,0,0,0,456,459,1,0,0,0,457,455,1,0,0,0,457, + 458,1,0,0,0,458,460,1,0,0,0,459,457,1,0,0,0,460,461,5,66,0,0,461,463,1, + 0,0,0,462,420,1,0,0,0,462,421,1,0,0,0,462,424,1,0,0,0,462,425,1,0,0,0,462, + 426,1,0,0,0,462,427,1,0,0,0,462,428,1,0,0,0,462,429,1,0,0,0,462,440,1,0, + 0,0,462,451,1,0,0,0,463,69,1,0,0,0,464,467,5,48,0,0,465,467,5,64,0,0,466, + 464,1,0,0,0,466,465,1,0,0,0,467,71,1,0,0,0,468,472,3,64,32,0,469,470,4, + 36,11,0,470,472,3,70,35,0,471,468,1,0,0,0,471,469,1,0,0,0,472,73,1,0,0, + 0,473,474,5,9,0,0,474,475,5,27,0,0,475,75,1,0,0,0,476,477,5,14,0,0,477, + 482,3,78,39,0,478,479,5,34,0,0,479,481,3,78,39,0,480,478,1,0,0,0,481,484, + 1,0,0,0,482,480,1,0,0,0,482,483,1,0,0,0,483,77,1,0,0,0,484,482,1,0,0,0, + 485,487,3,10,5,0,486,488,7,4,0,0,487,486,1,0,0,0,487,488,1,0,0,0,488,491, + 1,0,0,0,489,490,5,46,0,0,490,492,7,5,0,0,491,489,1,0,0,0,491,492,1,0,0, + 0,492,79,1,0,0,0,493,494,5,8,0,0,494,495,3,62,31,0,495,81,1,0,0,0,496,497, + 5,2,0,0,497,498,3,62,31,0,498,83,1,0,0,0,499,500,5,11,0,0,500,505,3,86, + 43,0,501,502,5,34,0,0,502,504,3,86,43,0,503,501,1,0,0,0,504,507,1,0,0,0, + 505,503,1,0,0,0,505,506,1,0,0,0,506,85,1,0,0,0,507,505,1,0,0,0,508,509, + 3,60,30,0,509,510,5,84,0,0,510,511,3,60,30,0,511,87,1,0,0,0,512,513,5,1, + 0,0,513,514,3,20,10,0,514,516,3,106,53,0,515,517,3,94,47,0,516,515,1,0, + 0,0,516,517,1,0,0,0,517,89,1,0,0,0,518,519,5,7,0,0,519,520,3,20,10,0,520, + 521,3,106,53,0,521,91,1,0,0,0,522,523,5,10,0,0,523,524,3,58,29,0,524,93, + 1,0,0,0,525,530,3,96,48,0,526,527,5,34,0,0,527,529,3,96,48,0,528,526,1, + 0,0,0,529,532,1,0,0,0,530,528,1,0,0,0,530,531,1,0,0,0,531,95,1,0,0,0,532, + 530,1,0,0,0,533,534,3,64,32,0,534,535,5,32,0,0,535,536,3,68,34,0,536,97, + 1,0,0,0,537,538,7,6,0,0,538,99,1,0,0,0,539,542,3,102,51,0,540,542,3,104, + 52,0,541,539,1,0,0,0,541,540,1,0,0,0,542,101,1,0,0,0,543,545,7,0,0,0,544, + 543,1,0,0,0,544,545,1,0,0,0,545,546,1,0,0,0,546,547,5,28,0,0,547,103,1, + 0,0,0,548,550,7,0,0,0,549,548,1,0,0,0,549,550,1,0,0,0,550,551,1,0,0,0,551, + 552,5,27,0,0,552,105,1,0,0,0,553,554,5,26,0,0,554,107,1,0,0,0,555,556,7, + 7,0,0,556,109,1,0,0,0,557,558,5,5,0,0,558,559,3,112,56,0,559,111,1,0,0, + 0,560,561,5,65,0,0,561,562,3,2,1,0,562,563,5,66,0,0,563,113,1,0,0,0,564, + 565,5,13,0,0,565,566,5,100,0,0,566,115,1,0,0,0,567,568,5,3,0,0,568,571, + 5,90,0,0,569,570,5,88,0,0,570,572,3,60,30,0,571,569,1,0,0,0,571,572,1,0, + 0,0,572,582,1,0,0,0,573,574,5,89,0,0,574,579,3,118,59,0,575,576,5,34,0, + 0,576,578,3,118,59,0,577,575,1,0,0,0,578,581,1,0,0,0,579,577,1,0,0,0,579, + 580,1,0,0,0,580,583,1,0,0,0,581,579,1,0,0,0,582,573,1,0,0,0,582,583,1,0, + 0,0,583,117,1,0,0,0,584,585,3,60,30,0,585,586,5,32,0,0,586,588,1,0,0,0, + 587,584,1,0,0,0,587,588,1,0,0,0,588,589,1,0,0,0,589,590,3,60,30,0,590,119, + 1,0,0,0,591,592,5,18,0,0,592,593,3,36,18,0,593,594,5,88,0,0,594,595,3,62, + 31,0,595,121,1,0,0,0,596,597,5,17,0,0,597,600,3,54,27,0,598,599,5,29,0, + 0,599,601,3,30,15,0,600,598,1,0,0,0,600,601,1,0,0,0,601,123,1,0,0,0,58, 135,144,162,174,183,191,197,205,207,212,219,224,235,241,249,251,262,269, - 280,283,289,301,307,317,321,326,336,344,357,361,365,372,376,383,389,396, - 404,412,420,437,448,459,464,468,473,484,489,493,507,518,532,543,546,551, - 573,581,584,589,602]; + 280,283,299,305,315,319,324,334,342,355,359,363,370,374,381,387,394,402, + 410,418,435,446,457,462,466,471,482,487,491,505,516,530,541,544,549,571, + 579,582,587,600]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -4124,19 +4107,20 @@ export class RegexBooleanExpressionContext extends ParserRuleContext { export class MatchBooleanExpressionContext extends ParserRuleContext { - public _queryString!: StringContext; + public _fieldExp!: QualifiedNameContext; + public _queryString!: ConstantContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); this.parser = parser; } - public valueExpression(): ValueExpressionContext { - return this.getTypedRuleContext(ValueExpressionContext, 0) as ValueExpressionContext; + public COLON(): TerminalNode { + return this.getToken(esql_parser.COLON, 0); } - public MATCH(): TerminalNode { - return this.getToken(esql_parser.MATCH, 0); + public qualifiedName(): QualifiedNameContext { + return this.getTypedRuleContext(QualifiedNameContext, 0) as QualifiedNameContext; } - public string_(): StringContext { - return this.getTypedRuleContext(StringContext, 0) as StringContext; + public constant(): ConstantContext { + return this.getTypedRuleContext(ConstantContext, 0) as ConstantContext; } public get ruleIndex(): number { return esql_parser.RULE_matchBooleanExpression; @@ -4484,9 +4468,6 @@ export class FunctionNameContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public MATCH(): TerminalNode { - return this.getToken(esql_parser.MATCH, 0); - } public identifierOrParameter(): IdentifierOrParameterContext { return this.getTypedRuleContext(IdentifierOrParameterContext, 0) as IdentifierOrParameterContext; } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts index c1c7340da78f..a8fa55128251 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts @@ -126,10 +126,10 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | INLINESTATS doubleField=', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | INLINESTATS doubleField=5 by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField', [ 'Unknown column [wrongField]', @@ -186,7 +186,7 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | INLINESTATS by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts index 79dc4e21fe9d..5384fdc136b4 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.metrics.ts @@ -117,11 +117,11 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => { await expectErrors('metrics a_index doubleField=', [ expect.any(String), - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('metrics a_index doubleField=5 by ', [ expect.any(String), - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts index c499f2477e14..c250166b8896 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.stats.ts @@ -117,10 +117,10 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | stats doubleField=', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | stats doubleField=5 by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); await expectErrors('from a_index | stats avg(doubleField) by wrongField', [ 'Unknown column [wrongField]', @@ -176,7 +176,7 @@ export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { const { expectErrors } = await setup(); await expectErrors('from a_index | stats by ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 3639a6be4d01..bf0e9782a339 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -223,7 +223,7 @@ { "query": "row", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -331,7 +331,7 @@ { "query": "row var = 1 in (", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Error: [in] function expects exactly 2 arguments, got 1." ], "warning": [] @@ -2645,7 +2645,7 @@ { "query": "from a_index | dissect", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -2739,7 +2739,7 @@ { "query": "from a_index | grok", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -3540,21 +3540,21 @@ { "query": "from a_index | where *+ doubleField", "error": [ - "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | where /+ doubleField", "error": [ - "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | where %+ doubleField", "error": [ - "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -4441,7 +4441,7 @@ { "query": "from a_index | eval ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -4484,7 +4484,7 @@ { "query": "from a_index | eval a=b, ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Unknown column [b]" ], "warning": [] @@ -4511,7 +4511,7 @@ { "query": "from a_index | eval a=round(doubleField), ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -5617,21 +5617,21 @@ { "query": "from a_index | eval *+ doubleField", "error": [ - "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '*' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | eval /+ doubleField", "error": [ - "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '/' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, { "query": "from a_index | eval %+ doubleField", "error": [ - "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: extraneous input '%' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -6955,7 +6955,7 @@ { "query": "from a_index | eval not", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", "Error: [not] function expects exactly one argument, got 0." ], "warning": [] @@ -6963,7 +6963,7 @@ { "query": "from a_index | eval in", "error": [ - "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -9012,7 +9012,7 @@ { "query": "from a_index | sort ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, @@ -9031,7 +9031,7 @@ { "query": "from a_index | sort doubleField, ", "error": [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}" ], "warning": [] }, diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 6cfa511c0386..9d737d542bd1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -306,7 +306,7 @@ describe('validation logic', () => { describe('row', () => { testErrorsAndWarnings('row', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('row missing_column', ['Unknown column [missing_column]']); testErrorsAndWarnings('row fn()', ['Unknown function [fn]']); @@ -335,7 +335,7 @@ describe('validation logic', () => { "SyntaxError: mismatched input '' expecting '('", ]); testErrorsAndWarnings('row var = 1 in (', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Error: [in] function expects exactly 2 arguments, got 1.', ]); testErrorsAndWarnings('row var = 1 not in ', [ @@ -690,7 +690,7 @@ describe('validation logic', () => { describe('dissect', () => { testErrorsAndWarnings('from a_index | dissect', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | dissect textField', [ "SyntaxError: missing QUOTED_STRING at ''", @@ -740,7 +740,7 @@ describe('validation logic', () => { describe('grok', () => { testErrorsAndWarnings('from a_index | grok', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | grok textField', [ "SyntaxError: missing QUOTED_STRING at ''", @@ -824,7 +824,7 @@ describe('validation logic', () => { } for (const wrongOp of ['*', '/', '%']) { testErrorsAndWarnings(`from a_index | where ${wrongOp}+ doubleField`, [ - `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, + `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, ]); } @@ -897,7 +897,7 @@ describe('validation logic', () => { describe('eval', () => { testErrorsAndWarnings('from a_index | eval ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval textField ', []); testErrorsAndWarnings('from a_index | eval b = textField', []); @@ -910,7 +910,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | eval a=b', ['Unknown column [b]']); testErrorsAndWarnings('from a_index | eval a=b, ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Unknown column [b]', ]); testErrorsAndWarnings('from a_index | eval a=round', ['Unknown column [round]']); @@ -919,7 +919,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | eval a=round(doubleField) ', []); testErrorsAndWarnings('from a_index | eval a=round(doubleField), ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval a=round(doubleField) + round(doubleField) ', []); testErrorsAndWarnings('from a_index | eval a=round(doubleField) + round(textField) ', [ @@ -982,7 +982,7 @@ describe('validation logic', () => { for (const wrongOp of ['*', '/', '%']) { testErrorsAndWarnings(`from a_index | eval ${wrongOp}+ doubleField`, [ - `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, + `SyntaxError: extraneous input '${wrongOp}' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}`, ]); } testErrorsAndWarnings( @@ -1201,11 +1201,11 @@ describe('validation logic', () => { [] ); testErrorsAndWarnings('from a_index | eval not', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", 'Error: [not] function expects exactly one argument, got 0.', ]); testErrorsAndWarnings('from a_index | eval in', [ - "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input 'in' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | eval textField in textField', [ @@ -1287,12 +1287,12 @@ describe('validation logic', () => { describe('sort', () => { testErrorsAndWarnings('from a_index | sort ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | sort "field" ', []); testErrorsAndWarnings('from a_index | sort wrongField ', ['Unknown column [wrongField]']); testErrorsAndWarnings('from a_index | sort doubleField, ', [ - "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', 'match', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + "SyntaxError: mismatched input '' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", ]); testErrorsAndWarnings('from a_index | sort doubleField, textField', []); for (const dir of ['desc', 'asc']) { diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.ts index bf5e2c597eb6..330e55de8615 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.ts @@ -74,7 +74,6 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'asc', 'desc', 'nulls_order', - 'match', ], euiThemeVars.euiColorAccentText, true // isBold From c19551f89a039c26ece79b505b4d9ff7ad729f47 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 11 Nov 2024 11:52:39 +0100 Subject: [PATCH 002/100] [APM] Attempt to fix service maps (#192859) ## Summary image --------- Co-authored-by: miriam.aparicio Co-authored-by: Miriam <31922082+MiriamAparicio@users.noreply.github.com> --- .../transform_service_map_responses.ts | 32 +++++++++++++++---- .../tests/service_maps/service_maps.spec.ts | 2 -- .../service_maps_kuery_filter.spec.ts | 1 - 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/observability_solution/apm/server/routes/service_map/transform_service_map_responses.ts b/x-pack/plugins/observability_solution/apm/server/routes/service_map/transform_service_map_responses.ts index 824ec0e7fcc1..c57a5bafb56d 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/service_map/transform_service_map_responses.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/service_map/transform_service_map_responses.ts @@ -86,9 +86,25 @@ export function getAllNodes( return allNodes; } -export function getServiceNodes(allNodes: ConnectionNode[]) { +export function getServiceNodes( + allNodes: ConnectionNode[], + discoveredServices: Array<{ + from: ExternalConnectionNode; + to: ServiceConnectionNode; + }> +) { + const connectionFromDiscoveredServices = discoveredServices + .filter(({ from, to }) => { + return ( + allNodes.some((node) => node.id === getConnectionNodeId(from)) && + !allNodes.some((node) => node.id === to[SERVICE_NAME]) + ); + }) + .map(({ to }) => ({ ...to, id: getConnectionNodeId(to) })); // List of nodes that are services - const serviceNodes = allNodes.filter((node) => SERVICE_NAME in node) as ServiceConnectionNode[]; + const serviceNodes = [...allNodes, ...connectionFromDiscoveredServices].filter( + (node) => SERVICE_NAME in node + ) as ServiceConnectionNode[]; return serviceNodes; } @@ -108,12 +124,16 @@ export function transformServiceMapResponses({ const { discoveredServices, services, connections, anomalies } = response; const allConnections = addMessagingConnections(connections, discoveredServices); const allNodes = getAllNodes(services, allConnections); - const serviceNodes = getServiceNodes(allNodes); + const serviceNodes = getServiceNodes(allNodes, discoveredServices); // List of nodes that are externals - const externalNodes = allNodes.filter( - (node) => SPAN_DESTINATION_SERVICE_RESOURCE in node - ) as ExternalConnectionNode[]; + const externalNodes = Array.from( + new Set( + allNodes.filter( + (node) => SPAN_DESTINATION_SERVICE_RESOURCE in node + ) as ExternalConnectionNode[] + ) + ); // 1. Map external nodes to internal services // 2. Collapse external nodes into one node based on span.destination.service.resource diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts index 334b73d8db4c..83965595020d 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.spec.ts @@ -153,13 +153,11 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) it('returns the correct data', () => { const elements: Array<{ data: Record }> = response.body.elements; - const serviceNames = uniq( elements .filter((element) => element.data['service.name'] !== undefined) .map((element) => element.data['service.name']) ).sort(); - expectSnapshot(serviceNames).toMatchInline(` Array [ "auditbeat", diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps_kuery_filter.spec.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps_kuery_filter.spec.ts index 4be91cbe7bab..b87e0de70495 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps_kuery_filter.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps_kuery_filter.spec.ts @@ -41,7 +41,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { } registry.when('Service Map', { config: 'trial', archives: [] }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/176982 describe('optional kuery param', () => { before(async () => { const events = timerange(start, end) From ff588ff041d67c75389e3a65788ddd40b470fee8 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Mon, 11 Nov 2024 11:57:03 +0000 Subject: [PATCH 003/100] [Entity Analytics] [Entity Store] Fix Asset Criticality index issue when setting up entity engines concurrently (#199486) ## Summary If the Entity Engine setup API was called in parallel for the host and user engine, there was an issue where both calls attempt to create the asset criticality index causing 'resource_already_exists_exception' to be thrown. This pull request fixes this by ignoring the `resource_already_exists_exception` when creating indices in our util. To test, setup both engines concurrently: ``` (curl -H 'Content-Type: application/json' -X POST -H 'kbn-xsrf: true' -H 'elastic-api-version: 2023-10-31' http://elastic:changeme@localhost:5601/api/entity_store/engines/host/init -d '{}' & curl -H 'Content-Type: application/json' -X POST -H 'kbn-xsrf: true' -H 'elastic-api-version: 2023-10-31' http://elastic:changeme@localhost:5601/api/entity_store/engines/user/init -d '{}' & wait) ``` --- .../elasticsearch_assets/entity_index.ts | 18 +++++++----------- .../utils/create_or_update_index.ts | 11 ++++++++++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/entity_index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/entity_index.ts index 7bc139ea08ad..6fb5935618df 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/entity_index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/entity_index.ts @@ -8,6 +8,7 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import type { EntityType } from '../../../../../common/api/entity_analytics'; import { getEntitiesIndexName } from '../utils'; +import { createOrUpdateIndex } from '../../utils/create_or_update_index'; interface Options { entityType: EntityType; @@ -17,18 +18,13 @@ interface Options { } export const createEntityIndex = async ({ entityType, esClient, namespace, logger }: Options) => { - try { - await esClient.indices.create({ + await createOrUpdateIndex({ + esClient, + logger, + options: { index: getEntitiesIndexName(entityType, namespace), - body: {}, - }); - } catch (e) { - if (e.meta.body.error.type === 'resource_already_exists_exception') { - logger.debug(`Index for ${entityType} already exists, skipping creation.`); - } else { - throw e; - } - } + }, + }); }; export const deleteEntityIndex = ({ entityType, esClient, namespace }: Options) => diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts index f9525e14ac6c..b6e49017c95a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_or_update_index.ts @@ -51,7 +51,16 @@ export const createOrUpdateIndex = async ({ ); } } else { - return esClient.indices.create(options); + try { + await esClient.indices.create(options); + } catch (err) { + // If the index already exists, we can ignore the error + if (err?.meta?.body?.error?.type === 'resource_already_exists_exception') { + logger.info(`${options.index} already exists`); + } else { + throw err; + } + } } } catch (err) { const error = transformError(err); From 961796d65d49d2ccc649f8dc4d71f0f5da7def48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=BCrkan=20YALAMAN?= Date: Mon, 11 Nov 2024 13:18:54 +0100 Subject: [PATCH 004/100] [Search Playground] Change route on selector changed in between search/chat modes (#197431) ## Summary Changes the route when Search/Chat is changed. It was the intended behaviour. Missed during the implementation ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../components/playground/playground.tsx | 8 ++++++-- .../public/applications/applications/index.tsx | 16 +++++++++++++--- .../public/applications/applications/routes.ts | 4 +++- .../search_playground/public/components/app.tsx | 11 ++++++++--- .../components/search_mode/search_mode.tsx | 1 + x-pack/plugins/search_playground/public/index.ts | 6 +++++- x-pack/plugins/search_playground/tsconfig.json | 2 +- 7 files changed, 37 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx index e8e72e5dfb37..c198062cb759 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/playground/playground.tsx @@ -15,7 +15,11 @@ import { KibanaLogic } from '../../../shared/kibana'; import { SearchPlaygroundPageTemplate } from './page_template'; -export const Playground: React.FC = () => { +interface PlaygroundProps { + pageMode?: 'chat' | 'search'; +} + +export const Playground: React.FC = ({ pageMode = 'chat' }) => { const { searchPlayground } = useValues(KibanaLogic); if (!searchPlayground) { @@ -35,7 +39,7 @@ export const Playground: React.FC = () => { customPageSections bottomBorder="extended" > - + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/index.tsx index c9676137e70f..a04ebf2e3edb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/index.tsx @@ -13,7 +13,13 @@ import { Routes, Route } from '@kbn/shared-ux-router'; import { NotFound } from './components/not_found'; import { Playground } from './components/playground/playground'; import { SearchApplicationsRouter } from './components/search_applications/search_applications_router'; -import { PLAYGROUND_PATH, ROOT_PATH, SEARCH_APPLICATIONS_PATH } from './routes'; +import { + PLAYGROUND_CHAT_PATH, + PLAYGROUND_PATH, + PLAYGROUND_SEARCH_PATH, + ROOT_PATH, + SEARCH_APPLICATIONS_PATH, +} from './routes'; export const Applications = () => { return ( @@ -22,8 +28,12 @@ export const Applications = () => { - - + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/routes.ts b/x-pack/plugins/enterprise_search/public/applications/applications/routes.ts index 5779f544c3f3..2df42a129938 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/applications/routes.ts @@ -17,7 +17,9 @@ export enum SearchApplicationViewTabs { export const SEARCH_APPLICATION_CREATION_PATH = `${SEARCH_APPLICATIONS_PATH}/new`; export const SEARCH_APPLICATION_PATH = `${SEARCH_APPLICATIONS_PATH}/:searchApplicationName`; export const SEARCH_APPLICATION_TAB_PATH = `${SEARCH_APPLICATION_PATH}/:tabId`; -export const PLAYGROUND_PATH = `${ROOT_PATH}playground`; +export const PLAYGROUND_PATH = `${ROOT_PATH}playground/`; +export const PLAYGROUND_CHAT_PATH = `${PLAYGROUND_PATH}chat`; +export const PLAYGROUND_SEARCH_PATH = `${PLAYGROUND_PATH}search`; export const SEARCH_APPLICATION_CONNECT_PATH = `${SEARCH_APPLICATION_PATH}/${SearchApplicationViewTabs.CONNECT}/:connectTabId`; export enum SearchApplicationConnectTabs { diff --git a/x-pack/plugins/search_playground/public/components/app.tsx b/x-pack/plugins/search_playground/public/components/app.tsx index 4f371ea5d15b..914a429845e9 100644 --- a/x-pack/plugins/search_playground/public/components/app.tsx +++ b/x-pack/plugins/search_playground/public/components/app.tsx @@ -18,10 +18,11 @@ import { Chat } from './chat'; import { SearchMode } from './search_mode/search_mode'; import { SearchPlaygroundSetupPage } from './setup_page/search_playground_setup_page'; import { usePageMode } from '../hooks/use_page_mode'; +import { useKibana } from '../hooks/use_kibana'; export interface AppProps { showDocs?: boolean; - pageMode?: PlaygroundPageMode; + pageMode?: 'chat' | 'search'; } export enum ViewMode { @@ -33,6 +34,7 @@ export const App: React.FC = ({ showDocs = false, pageMode = PlaygroundPageMode.chat, }) => { + const { services } = useKibana(); const [selectedMode, setSelectedMode] = useState(ViewMode.chat); const { data: connectors } = useLoadConnectors(); const hasSelectedIndices = Boolean( @@ -41,7 +43,10 @@ export const App: React.FC = ({ }).length ); const handleModeChange = (id: ViewMode) => setSelectedMode(id); - const handlePageModeChange = (mode: PlaygroundPageMode) => setSelectedPageMode(mode); + const handlePageModeChange = (mode: PlaygroundPageMode) => { + services.application?.navigateToUrl(`./${mode}`); + setSelectedPageMode(mode); + }; const { showSetupPage, pageMode: selectedPageMode, @@ -49,7 +54,7 @@ export const App: React.FC = ({ } = usePageMode({ hasSelectedIndices, hasConnectors: Boolean(connectors?.length), - initialPageMode: pageMode, + initialPageMode: pageMode === 'chat' ? PlaygroundPageMode.chat : PlaygroundPageMode.search, }); const restrictedWidth = selectedPageMode === PlaygroundPageMode.search && selectedMode === 'chat'; diff --git a/x-pack/plugins/search_playground/public/components/search_mode/search_mode.tsx b/x-pack/plugins/search_playground/public/components/search_mode/search_mode.tsx index 94e6337f1a03..8a27f3ac3d83 100644 --- a/x-pack/plugins/search_playground/public/components/search_mode/search_mode.tsx +++ b/x-pack/plugins/search_playground/public/components/search_mode/search_mode.tsx @@ -69,6 +69,7 @@ export const SearchMode: React.FC = () => { name={ChatFormFields.searchQuery} render={({ field }) => ( Date: Mon, 11 Nov 2024 09:29:21 -0300 Subject: [PATCH 005/100] [Security Solution] Rule Updates in bulk with conflicts (#196776) Resolves: https://github.com/elastic/kibana/issues/180589 ## Summary - Handles bulk updating of rules with conflicts in the Rule Upgrades table. See detailed requirements implemented in ticket linked above. - Changes default `pick_version` of both the `/upgrade/_perform` endpoint, and of the request payloads for that endpoint from the frontend, from `TARGET` to `MERGED`, when the `isPrebuiltRulesCustomizationEnabled` is `true`. - **Also:** handles issue in `/upgrade/_perform` endpoint with the `index` and `data_view_id` fields. See file: `x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/diffable_rule_fields_mappings.ts`. **See demo video:** https://www.loom.com/share/90d94d2a8f16442b9a43a425eeab6697 **New copy in warning modal** image **Newly added tooltips:** ![image](https://github.com/user-attachments/assets/7ada117e-57a7-4699-ad08-312c734586d9) ![image](https://github.com/user-attachments/assets/c8ed80ac-c1c3-48f1-8f8e-2433415a6772) ![image](https://github.com/user-attachments/assets/d77ed6f0-5d65-4933-9012-f6cd153a9350) ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) - [ ] This will appear in the **Release Notes** and follow the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Nastasha Solomon <79124755+nastasha-solomon@users.noreply.github.com> Co-authored-by: Dmitrii --- .github/CODEOWNERS | 1 - .../machine_learning}/affected_job_ids.ts | 0 .../rule_management/api/api.ts | 16 +- .../use_perform_all_rules_upgrade_mutation.ts | 49 ------ ...perform_specific_rules_upgrade_mutation.ts | 4 +- .../use_perform_rule_upgrade.ts | 23 +-- .../modals/ml_job_upgrade_modal/index.tsx | 0 .../ml_job_upgrade_modal/translations.tsx | 2 +- .../modals/upgrade_conflicts_modal/index.tsx | 36 +++++ .../upgrade_conflicts_modal/translations.tsx | 37 +++++ .../translations.ts | 27 ++++ .../upgrade_prebuilt_rules_table_buttons.tsx | 129 ++++++++++++--- .../upgrade_prebuilt_rules_table_context.tsx | 128 ++++++++++----- .../use_upgrade_modals.tsx | 45 ++++++ ...e_upgrade_prebuilt_rules_table_columns.tsx | 22 ++- .../ml_job_compatibility_callout/index.tsx | 2 +- .../detection_engine/rules/translations.ts | 7 + .../create_upgradeable_rules_payload.ts | 7 +- .../perform_rule_upgrade_route.ts | 8 +- .../prebuilt_rules/api/register_routes.ts | 9 +- .../security_solution/server/routes/index.ts | 2 +- ...low_with_prebuilt_rule_customization.cy.ts | 153 +++++++++++++++++- .../cypress/screens/rule_updates.ts | 2 + .../cypress/tasks/prebuilt_rules.ts | 9 ++ 24 files changed, 555 insertions(+), 163 deletions(-) rename x-pack/plugins/security_solution/{public/detections/components/callouts/ml_job_compatibility_callout => common/machine_learning}/affected_job_ids.ts (100%) delete mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_all_rules_upgrade_mutation.ts rename x-pack/plugins/security_solution/public/{detections/components => detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table}/modals/ml_job_upgrade_modal/index.tsx (100%) rename x-pack/plugins/security_solution/public/{detections/components => detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table}/modals/ml_job_upgrade_modal/translations.tsx (95%) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/translations.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_modals.tsx diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 73a670d14534..abc6749d52ea 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1843,7 +1843,6 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/security_solution/public/detection_engine/rule_management_ui @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/detection_engine/rule_monitoring @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/detections/components/callouts @elastic/security-detection-rule-management -/x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/detections/components/rules @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/detections/components/rules/rule_preview @elastic/security-detection-engine /x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules @elastic/security-detection-rule-management diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts b/x-pack/plugins/security_solution/common/machine_learning/affected_job_ids.ts similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/affected_job_ids.ts rename to x-pack/plugins/security_solution/common/machine_learning/affected_job_ids.ts diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts index 1e2ee1be7a47..aea4b6672659 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts @@ -16,6 +16,7 @@ import type { ActionResult } from '@kbn/actions-plugin/server'; import { convertRulesFilterToKQL } from '../../../../common/detection_engine/rule_management/rule_filtering'; import type { UpgradeSpecificRulesRequest, + PickVersionValues, PerformRuleUpgradeResponseBody, InstallSpecificRulesRequest, PerformRuleInstallationResponseBody, @@ -678,18 +679,9 @@ export const performInstallSpecificRules = async ( }), }); -export const performUpgradeAllRules = async (): Promise => - KibanaServices.get().http.fetch(PERFORM_RULE_UPGRADE_URL, { - method: 'POST', - version: '1', - body: JSON.stringify({ - mode: 'ALL_RULES', - pick_version: 'TARGET', - }), - }); - export const performUpgradeSpecificRules = async ( - rules: UpgradeSpecificRulesRequest['rules'] + rules: UpgradeSpecificRulesRequest['rules'], + pickVersion: PickVersionValues ): Promise => KibanaServices.get().http.fetch(PERFORM_RULE_UPGRADE_URL, { method: 'POST', @@ -697,7 +689,7 @@ export const performUpgradeSpecificRules = async ( body: JSON.stringify({ mode: 'SPECIFIC_RULES', rules, - pick_version: 'TARGET', // Setting fixed 'TARGET' temporarily for Milestone 2 + pick_version: pickVersion, }), }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_all_rules_upgrade_mutation.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_all_rules_upgrade_mutation.ts deleted file mode 100644 index 7e5385bb0c75..000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_all_rules_upgrade_mutation.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import type { UseMutationOptions } from '@tanstack/react-query'; -import { useMutation } from '@tanstack/react-query'; -import type { PerformRuleUpgradeResponseBody } from '../../../../../../common/api/detection_engine/prebuilt_rules'; -import { PERFORM_RULE_UPGRADE_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules/urls'; -import { useInvalidateFindRulesQuery } from '../use_find_rules_query'; -import { useInvalidateFetchRuleManagementFiltersQuery } from '../use_fetch_rule_management_filters_query'; -import { useInvalidateFetchRulesSnoozeSettingsQuery } from '../use_fetch_rules_snooze_settings_query'; -import { useInvalidateFetchPrebuiltRulesUpgradeReviewQuery } from './use_fetch_prebuilt_rules_upgrade_review_query'; -import { useInvalidateFetchPrebuiltRulesStatusQuery } from './use_fetch_prebuilt_rules_status_query'; -import { performUpgradeAllRules } from '../../api'; -import { useInvalidateFetchCoverageOverviewQuery } from '../use_fetch_coverage_overview_query'; - -export const PERFORM_ALL_RULES_UPGRADE_KEY = ['POST', 'ALL_RULES', PERFORM_RULE_UPGRADE_URL]; - -export const usePerformAllRulesUpgradeMutation = ( - options?: UseMutationOptions -) => { - const invalidateFindRulesQuery = useInvalidateFindRulesQuery(); - const invalidateFetchRulesSnoozeSettings = useInvalidateFetchRulesSnoozeSettingsQuery(); - const invalidateFetchRuleManagementFilters = useInvalidateFetchRuleManagementFiltersQuery(); - const invalidateFetchPrebuiltRulesUpgradeReview = - useInvalidateFetchPrebuiltRulesUpgradeReviewQuery(); - const invalidateRuleStatus = useInvalidateFetchPrebuiltRulesStatusQuery(); - const invalidateFetchCoverageOverviewQuery = useInvalidateFetchCoverageOverviewQuery(); - - return useMutation(() => performUpgradeAllRules(), { - ...options, - mutationKey: PERFORM_ALL_RULES_UPGRADE_KEY, - onSettled: (...args) => { - invalidateFindRulesQuery(); - invalidateFetchRulesSnoozeSettings(); - invalidateFetchRuleManagementFilters(); - - invalidateFetchPrebuiltRulesUpgradeReview(); - invalidateRuleStatus(); - invalidateFetchCoverageOverviewQuery(); - - if (options?.onSettled) { - options.onSettled(...args); - } - }, - }); -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_specific_rules_upgrade_mutation.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_specific_rules_upgrade_mutation.ts index c10b92ea914f..08338ab9a932 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_specific_rules_upgrade_mutation.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_specific_rules_upgrade_mutation.ts @@ -8,6 +8,7 @@ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { PerformRuleUpgradeResponseBody, + PickVersionValues, UpgradeSpecificRulesRequest, } from '../../../../../../common/api/detection_engine/prebuilt_rules'; import { PERFORM_RULE_UPGRADE_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules/urls'; @@ -26,6 +27,7 @@ export const PERFORM_SPECIFIC_RULES_UPGRADE_KEY = [ ]; export const usePerformSpecificRulesUpgradeMutation = ( + pickVersion: PickVersionValues, options?: UseMutationOptions< PerformRuleUpgradeResponseBody, Error, @@ -43,7 +45,7 @@ export const usePerformSpecificRulesUpgradeMutation = ( return useMutation( (rulesToUpgrade: UpgradeSpecificRulesRequest['rules']) => { - return performUpgradeSpecificRules(rulesToUpgrade); + return performUpgradeSpecificRules(rulesToUpgrade, pickVersion); }, { ...options, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/prebuilt_rules/use_perform_rule_upgrade.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/prebuilt_rules/use_perform_rule_upgrade.ts index aa9e38217a19..f82812f7ac9d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/prebuilt_rules/use_perform_rule_upgrade.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/prebuilt_rules/use_perform_rule_upgrade.ts @@ -4,29 +4,20 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { PickVersionValues } from '../../../../../common/api/detection_engine'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; -import { usePerformAllRulesUpgradeMutation } from '../../api/hooks/prebuilt_rules/use_perform_all_rules_upgrade_mutation'; import { usePerformSpecificRulesUpgradeMutation } from '../../api/hooks/prebuilt_rules/use_perform_specific_rules_upgrade_mutation'; import * as i18n from './translations'; -export const usePerformUpgradeAllRules = () => { - const { addError, addSuccess } = useAppToasts(); - - return usePerformAllRulesUpgradeMutation({ - onError: (err) => { - addError(err, { title: i18n.RULE_UPGRADE_FAILED }); - }, - onSuccess: (result) => { - addSuccess(getSuccessToastMessage(result)); - }, - }); -}; - -export const usePerformUpgradeSpecificRules = () => { +export const usePerformUpgradeSpecificRules = ({ + pickVersion, +}: { + pickVersion: PickVersionValues; +}) => { const { addError, addSuccess } = useAppToasts(); - return usePerformSpecificRulesUpgradeMutation({ + return usePerformSpecificRulesUpgradeMutation(pickVersion, { onError: (err) => { addError(err, { title: i18n.RULE_UPGRADE_FAILED }); }, diff --git a/x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/ml_job_upgrade_modal/index.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/ml_job_upgrade_modal/index.tsx diff --git a/x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal/translations.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/ml_job_upgrade_modal/translations.tsx similarity index 95% rename from x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal/translations.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/ml_job_upgrade_modal/translations.tsx index 8163eca279cf..caf94e5c1a26 100644 --- a/x-pack/plugins/security_solution/public/detections/components/modals/ml_job_upgrade_modal/translations.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/ml_job_upgrade_modal/translations.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { MlJobCompatibilityLink } from '../../../../common/components/links_to_docs'; +import { MlJobCompatibilityLink } from '../../../../../../../common/components/links_to_docs'; export const ML_JOB_UPGRADE_MODAL_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.mlJobUpgradeModal.messageTitle', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/index.tsx new file mode 100644 index 000000000000..0c664398c51f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/index.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiConfirmModal, EuiText } from '@elastic/eui'; +import React, { memo } from 'react'; +import * as i18n from './translations'; + +export interface UpgradeConflictsModalProps { + onCancel: ( + event?: React.KeyboardEvent | React.MouseEvent + ) => void; + onConfirm?: (event: React.MouseEvent) => void; +} + +const UpgradeConflictsModalComponent = ({ onCancel, onConfirm }: UpgradeConflictsModalProps) => { + return ( + + {i18n.UPGRADE_CONFLICTS_MODAL_BODY} + + ); +}; + +export const UpgradeConflictsModal = memo(UpgradeConflictsModalComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/translations.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/translations.tsx new file mode 100644 index 000000000000..bc5738b879cc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/modals/upgrade_conflicts_modal/translations.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const UPGRADE_CONFLICTS_MODAL_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.upgradeConflictsModal.messageTitle', + { + defaultMessage: 'Update rules without conflicts?', + } +); + +export const UPGRADE_CONFLICTS_MODAL_CANCEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.upgradeConflictsModal.cancelTitle', + { + defaultMessage: 'Cancel', + } +); + +export const UPGRADE_CONFLICTS_MODAL_CONFIRM = i18n.translate( + 'xpack.securitySolution.detectionEngine.upgradeConflictsModal.confirmTitle', + { + defaultMessage: 'Update rules without conflicts', + } +); + +export const UPGRADE_CONFLICTS_MODAL_BODY = i18n.translate( + 'xpack.securitySolution.detectionEngine.upgradeConflictsModal.affectedJobsTitle', + { + defaultMessage: + "Some of the selected rules have conflicts and, for that reason, won't be updated. Resolve the conflicts to properly update the rules.", + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/translations.ts index 026c35f664bb..5db5d0eee748 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/translations.ts @@ -24,6 +24,27 @@ export const UPDATE_SELECTED_RULES = (numberOfSelectedRules: number) => { ); }; +export const BULK_UPDATE_BUTTON_TOOLTIP_NO_PERMISSIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.bulkButtons.noPermissions', + { + defaultMessage: "You don't have permissions to update rules", + } +); + +export const BULK_UPDATE_ALL_RULES_BUTTON_TOOLTIP_CONFLICTS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.bulkButtons.allRules.conflicts', + { + defaultMessage: 'All rules have conflicts. Update them individually.', + } +); + +export const BULK_UPDATE_SELECTED_RULES_BUTTON_TOOLTIP_CONFLICTS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.bulkButtons.selectedRules.conflicts', + { + defaultMessage: 'All selected rules have conflicts. Update them individually.', + } +); + export const SEARCH_PLACEHOLDER = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.upgradeRules.searchBarPlaceholder', { @@ -37,6 +58,12 @@ export const UPDATE_BUTTON_LABEL = i18n.translate( defaultMessage: 'Update', } ); +export const UPDATE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.updateError', + { + defaultMessage: 'Update error', + } +); export const UPDATE_FLYOUT_PER_FIELD_TOOLTIP_DESCRIPTION = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.perFieldTooltip', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_buttons.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_buttons.tsx index 1d0e6adeabce..8a1d20114965 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_buttons.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_buttons.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import React, { useCallback } from 'react'; import type { RuleUpgradeState } from '../../../../rule_management/model/prebuilt_rule_upgrade'; import { useUserData } from '../../../../../detections/components/user_info'; @@ -20,8 +20,15 @@ export const UpgradePrebuiltRulesTableButtons = ({ selectedRules, }: UpgradePrebuiltRulesTableButtonsProps) => { const { - state: { hasRulesToUpgrade, loadingRules, isRefetching, isUpgradingSecurityPackages }, - actions: { upgradeAllRules, upgradeRules }, + state: { + ruleUpgradeInfos, + hasRulesToUpgrade, + loadingRules, + isRefetching, + isUpgradingSecurityPackages, + isPrebuiltRulesCustomizationEnabled, + }, + actions: { upgradeRules }, } = useUpgradePrebuiltRulesTableContext(); const [{ loading: isUserDataLoading, canUserCRUD }] = useUserData(); const canUserEditRules = canUserCRUD && !isUserDataLoading; @@ -31,39 +38,115 @@ export const UpgradePrebuiltRulesTableButtons = ({ const isRuleUpgrading = loadingRules.length > 0; const isRequestInProgress = isRuleUpgrading || isRefetching || isUpgradingSecurityPackages; + const doAllSelectedRulesHaveConflicts = + isPrebuiltRulesCustomizationEnabled && isAllRuleHaveConflicts(selectedRules); + const doAllRulesHaveConflicts = + isPrebuiltRulesCustomizationEnabled && isAllRuleHaveConflicts(ruleUpgradeInfos); + + const { selectedRulesButtonTooltip, allRulesButtonTooltip } = useBulkUpdateButtonsTooltipContent({ + canUserEditRules, + doAllSelectedRulesHaveConflicts, + doAllRulesHaveConflicts, + isPrebuiltRulesCustomizationEnabled, + }); + const upgradeSelectedRules = useCallback( () => upgradeRules(selectedRules.map((rule) => rule.rule_id)), [selectedRules, upgradeRules] ); + const upgradeAllRules = useCallback( + // Upgrade all rules, ignoring filter and selection + () => upgradeRules(ruleUpgradeInfos.map((rule) => rule.rule_id)), + [ruleUpgradeInfos, upgradeRules] + ); + return ( {shouldDisplayUpgradeSelectedRulesButton ? ( - - <> - {i18n.UPDATE_SELECTED_RULES(numberOfSelectedRules)} - {isRuleUpgrading ? : undefined} - - + + + <> + {i18n.UPDATE_SELECTED_RULES(numberOfSelectedRules)} + {isRuleUpgrading ? : undefined} + + + ) : null} - - {i18n.UPDATE_ALL} - {isRuleUpgrading ? : undefined} - + + + {i18n.UPDATE_ALL} + {isRuleUpgrading ? : undefined} + + ); }; + +const useBulkUpdateButtonsTooltipContent = ({ + canUserEditRules, + doAllSelectedRulesHaveConflicts, + doAllRulesHaveConflicts, + isPrebuiltRulesCustomizationEnabled, +}: { + canUserEditRules: boolean | null; + doAllSelectedRulesHaveConflicts: boolean; + doAllRulesHaveConflicts: boolean; + isPrebuiltRulesCustomizationEnabled: boolean; +}) => { + if (!canUserEditRules) { + return { + selectedRulesButtonTooltip: i18n.BULK_UPDATE_BUTTON_TOOLTIP_NO_PERMISSIONS, + allRulesButtonTooltip: i18n.BULK_UPDATE_BUTTON_TOOLTIP_NO_PERMISSIONS, + }; + } + + if (!isPrebuiltRulesCustomizationEnabled) { + return { + selectedRulesButtonTooltip: undefined, + allRulesButtonTooltip: undefined, + }; + } + + if (doAllRulesHaveConflicts) { + return { + selectedRulesButtonTooltip: i18n.BULK_UPDATE_SELECTED_RULES_BUTTON_TOOLTIP_CONFLICTS, + allRulesButtonTooltip: i18n.BULK_UPDATE_ALL_RULES_BUTTON_TOOLTIP_CONFLICTS, + }; + } + + if (doAllSelectedRulesHaveConflicts) { + return { + selectedRulesButtonTooltip: i18n.BULK_UPDATE_SELECTED_RULES_BUTTON_TOOLTIP_CONFLICTS, + allRulesButtonTooltip: undefined, + }; + } + + return { + selectedRulesButtonTooltip: undefined, + allRulesButtonTooltip: undefined, + }; +}; + +function isAllRuleHaveConflicts(rules: Array<{ diff: { num_fields_with_conflicts: number } }>) { + return rules.every((rule) => rule.diff.num_fields_with_conflicts > 0); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx index 6ec9ffdd02e6..cbb0350da173 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx @@ -9,35 +9,36 @@ import type { Dispatch, SetStateAction } from 'react'; import React, { createContext, useCallback, useContext, useMemo, useState } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; import { useIsPrebuiltRulesCustomizationEnabled } from '../../../../rule_management/hooks/use_is_prebuilt_rules_customization_enabled'; +import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; +import type { RuleUpgradeInfoForReview } from '../../../../../../common/api/detection_engine'; import type { RulesUpgradeState } from '../../../../rule_management/model/prebuilt_rule_upgrade'; import { RuleUpgradeConflictsResolverTab } from '../../../../rule_management/components/rule_details/three_way_diff/rule_upgrade_conflicts_resolver_tab'; import { PerFieldRuleDiffTab } from '../../../../rule_management/components/rule_details/per_field_rule_diff_tab'; import { useIsUpgradingSecurityPackages } from '../../../../rule_management/logic/use_upgrade_security_packages'; -import { useInstalledSecurityJobs } from '../../../../../common/components/ml/hooks/use_installed_security_jobs'; -import { useBoolState } from '../../../../../common/hooks/use_bool_state'; -import { affectedJobIds } from '../../../../../detections/components/callouts/ml_job_compatibility_callout/affected_job_ids'; import type { RuleResponse, RuleSignatureId, } from '../../../../../../common/api/detection_engine/model/rule_schema'; import { invariant } from '../../../../../../common/utils/invariant'; -import { - usePerformUpgradeAllRules, - usePerformUpgradeSpecificRules, -} from '../../../../rule_management/logic/prebuilt_rules/use_perform_rule_upgrade'; +import { usePerformUpgradeSpecificRules } from '../../../../rule_management/logic/prebuilt_rules/use_perform_rule_upgrade'; import { usePrebuiltRulesUpgradeReview } from '../../../../rule_management/logic/prebuilt_rules/use_prebuilt_rules_upgrade_review'; import type { UpgradePrebuiltRulesTableFilterOptions } from './use_filter_prebuilt_rules_to_upgrade'; import { useFilterPrebuiltRulesToUpgrade } from './use_filter_prebuilt_rules_to_upgrade'; -import { useAsyncConfirmation } from '../rules_table/use_async_confirmation'; import { TabContentPadding } from '../../../../rule_management/components/rule_details/rule_details_flyout'; import { RuleDiffTab } from '../../../../rule_management/components/rule_details/rule_diff_tab'; -import { MlJobUpgradeModal } from '../../../../../detections/components/modals/ml_job_upgrade_modal'; +import { MlJobUpgradeModal } from './modals/ml_job_upgrade_modal'; +import { UpgradeConflictsModal } from './modals/upgrade_conflicts_modal'; import * as ruleDetailsI18n from '../../../../rule_management/components/rule_details/translations'; import * as i18n from './translations'; import { usePrebuiltRulesUpgradeState } from './use_prebuilt_rules_upgrade_state'; import { useRulePreviewFlyout } from '../use_rule_preview_flyout'; +import { useMlJobUpgradeModal, useUpgradeConflictsModal } from './use_upgrade_modals'; export interface UpgradePrebuiltRulesTableState { + /** + * Rule upgrade state (all rules available for upgrade) + */ + ruleUpgradeInfos: RuleUpgradeInfoForReview[]; /** * Rule upgrade state after applying `filterOptions` */ @@ -90,7 +91,6 @@ export const PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR = 'updatePrebuiltRulePreview'; export interface UpgradePrebuiltRulesTableActions { reFetchRules: () => void; upgradeRules: (ruleIds: RuleSignatureId[]) => void; - upgradeAllRules: () => void; setFilterOptions: Dispatch>; openRulePreview: (ruleId: string) => void; } @@ -118,6 +118,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ tags: [], ruleSource: [], }); + const { addError } = useAppToasts(); const isUpgradingSecurityPackages = useIsUpgradingSecurityPackages(); @@ -142,21 +143,41 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ const { rulesUpgradeState, setRuleFieldResolvedValue } = usePrebuiltRulesUpgradeState(filteredRuleUpgradeInfos); - // Wrapper to add confirmation modal for users who may be running older ML Jobs that would - // be overridden by updating their rules. For details, see: https://github.com/elastic/kibana/issues/128121 - const [isUpgradeModalVisible, showUpgradeModal, hideUpgradeModal] = useBoolState(false); - const { loading: loadingJobs, jobs } = useInstalledSecurityJobs(); - const legacyJobsInstalled = jobs.filter((job) => affectedJobIds.includes(job.id)); + const { + isVisible: isLegacyMLJobsModalVisible, + legacyJobsInstalled, + confirmLegacyMLJobs, + handleConfirm: handleLegacyMLJobsConfirm, + handleCancel: handleLegacyMLJobsCancel, + loadingJobs, + } = useMlJobUpgradeModal(); - const [confirmUpgrade, handleUpgradeConfirm, handleUpgradeCancel] = useAsyncConfirmation({ - onInit: showUpgradeModal, - onFinish: hideUpgradeModal, - }); + const { + isVisible: isConflictsModalVisible, + confirmConflictsUpgrade, + handleConfirm: handleConflictsConfirm, + handleCancel: handleConflictsCancel, + } = useUpgradeConflictsModal(); - const shouldConfirmUpgrade = legacyJobsInstalled.length > 0; + const shouldConfirmMLJobs = legacyJobsInstalled.length > 0; + const getRulesWithConflicts = useCallback( + (ruleIds?: RuleSignatureId[]) => { + const rulesToUpgrade = + ruleIds?.map((ruleId) => { + const rule = rulesUpgradeState[ruleId]; + invariant(rule, `Rule with ID ${ruleId} not found.`); - const { mutateAsync: upgradeAllRulesRequest } = usePerformUpgradeAllRules(); - const { mutateAsync: upgradeSpecificRulesRequest } = usePerformUpgradeSpecificRules(); + return rule; + }) ?? []; + + return rulesToUpgrade.filter((rule) => rule.diff.num_fields_with_conflicts > 0); + }, + [rulesUpgradeState] + ); + + const { mutateAsync: upgradeSpecificRulesRequest } = usePerformUpgradeSpecificRules({ + pickVersion: isPrebuiltRulesCustomizationEnabled ? 'MERGED' : 'TARGET', + }); const upgradeRules = useCallback( async (ruleIds: RuleSignatureId[]) => { @@ -169,32 +190,47 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ })); setLoadingRules((prev) => [...prev, ...rulesToUpgrade.map((r) => r.rule_id)]); try { - if (shouldConfirmUpgrade && !(await confirmUpgrade())) { + // Handle MLJobs modal + if (shouldConfirmMLJobs && !(await confirmLegacyMLJobs())) { return; } - await upgradeSpecificRulesRequest(rulesToUpgrade); + + // Handle Rule Upgrades modal + const rulesWithConflicts = getRulesWithConflicts(ruleIds); + if ( + isPrebuiltRulesCustomizationEnabled && + rulesWithConflicts.length > 0 && + !(await confirmConflictsUpgrade()) + ) { + return; + } + + // Prepare payload for upgrade with rules with no conflicts + const ruleIdsWithConflicts = new Set(rulesWithConflicts.map((rule) => rule.rule_id)); + const rulesToUpgradeWithNoConflicts = isPrebuiltRulesCustomizationEnabled + ? rulesToUpgrade.filter((rule) => !ruleIdsWithConflicts.has(rule.rule_id)) + : rulesToUpgrade; + await upgradeSpecificRulesRequest(rulesToUpgradeWithNoConflicts); + } catch (err) { + addError(err, { title: i18n.UPDATE_ERROR }); } finally { setLoadingRules((prev) => prev.filter((id) => !rulesToUpgrade.some((r) => r.rule_id === id)) ); } }, - [confirmUpgrade, shouldConfirmUpgrade, rulesUpgradeState, upgradeSpecificRulesRequest] + [ + confirmLegacyMLJobs, + confirmConflictsUpgrade, + shouldConfirmMLJobs, + getRulesWithConflicts, + rulesUpgradeState, + upgradeSpecificRulesRequest, + isPrebuiltRulesCustomizationEnabled, + addError, + ] ); - const upgradeAllRules = useCallback(async () => { - // Unselect all rules so that the table doesn't show the "bulk actions" bar - setLoadingRules((prev) => [...prev, ...ruleUpgradeInfos.map((r) => r.rule_id)]); - try { - if (shouldConfirmUpgrade && !(await confirmUpgrade())) { - return; - } - await upgradeAllRulesRequest(); - } finally { - setLoadingRules([]); - } - }, [confirmUpgrade, ruleUpgradeInfos, shouldConfirmUpgrade, upgradeAllRulesRequest]); - const ruleActionsFactory = useCallback( (rule: RuleResponse, closeRulePreview: () => void) => ( ({ reFetchRules: refetch, upgradeRules, - upgradeAllRules, setFilterOptions, openRulePreview, }), - [refetch, upgradeRules, upgradeAllRules, openRulePreview] + [refetch, upgradeRules, openRulePreview] ); const providerValue = useMemo(() => { return { state: { + ruleUpgradeInfos, rulesUpgradeState, hasRulesToUpgrade: isFetched && ruleUpgradeInfos.length > 0, filterOptions, @@ -343,11 +379,17 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ return ( <> - {isUpgradeModalVisible && ( + {isLegacyMLJobsModalVisible && ( + )} + {isConflictsModalVisible && ( + )} {children} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_modals.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_modals.tsx new file mode 100644 index 000000000000..3e20d914dabc --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_modals.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useInstalledSecurityJobs } from '../../../../../common/components/ml/hooks/use_installed_security_jobs'; +import { useBoolState } from '../../../../../common/hooks/use_bool_state'; +import { affectedJobIds } from '../../../../../../common/machine_learning/affected_job_ids'; +import { useAsyncConfirmation } from '../rules_table/use_async_confirmation'; + +export const useMlJobUpgradeModal = () => { + const [isVisible, showModal, hideModal] = useBoolState(false); + const { loading: loadingJobs, jobs } = useInstalledSecurityJobs(); + const legacyJobsInstalled = jobs.filter((job) => affectedJobIds.includes(job.id)); + const [confirmLegacyMLJobs, handleConfirm, handleCancel] = useAsyncConfirmation({ + onInit: showModal, + onFinish: hideModal, + }); + + return { + isVisible, + legacyJobsInstalled, + confirmLegacyMLJobs, + handleConfirm, + handleCancel, + loadingJobs, + }; +}; + +export const useUpgradeConflictsModal = () => { + const [isVisible, showModal, hideModal] = useBoolState(false); + const [confirmConflictsUpgrade, handleConfirm, handleCancel] = useAsyncConfirmation({ + onInit: showModal, + onFinish: hideModal, + }); + + return { + isVisible, + confirmConflictsUpgrade, + handleConfirm, + handleCancel, + }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx index 579f571f80e7..c2b1f8e3dbe4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/use_upgrade_prebuilt_rules_table_columns.tsx @@ -160,15 +160,21 @@ const createUpgradeButtonColumn = ( /> ); + const tooltipContent = isDisabledByConflicts + ? i18n.UPDATE_RULE_BUTTON_TOOLTIP_CONFLICTS + : undefined; + return ( - upgradeRules([ruleId])} - data-test-subj={`upgradeSinglePrebuiltRuleButton-${ruleId}`} - > - {isRuleUpgrading ? spinner : i18n.UPDATE_RULE_BUTTON} - + + upgradeRules([ruleId])} + data-test-subj={`upgradeSinglePrebuiltRuleButton-${ruleId}`} + > + {isRuleUpgrading ? spinner : i18n.UPDATE_RULE_BUTTON} + + ); }, width: '10%', diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx index df4eb0a24968..92d42b70d240 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/ml_job_compatibility_callout/index.tsx @@ -10,7 +10,7 @@ import React, { memo } from 'react'; import type { CallOutMessage } from '../../../../common/components/callouts'; import { CallOutSwitcher } from '../../../../common/components/callouts'; import { useInstalledSecurityJobs } from '../../../../common/components/ml/hooks/use_installed_security_jobs'; -import { affectedJobIds } from './affected_job_ids'; +import { affectedJobIds } from '../../../../../common/machine_learning/affected_job_ids'; import * as i18n from './translations'; const mlJobCompatibilityCalloutMessage: CallOutMessage = { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index b6a3bbdc0ad9..cb0570855dab 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -1435,6 +1435,13 @@ export const UPDATE_RULE_BUTTON = i18n.translate( } ); +export const UPDATE_RULE_BUTTON_TOOLTIP_CONFLICTS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.button.conflicts', + { + defaultMessage: 'Rule has conflicts. Resolve them manually.', + } +); + export const GO_BACK_TO_RULES_TABLE_BUTTON = i18n.translate( 'xpack.securitySolution.addRules.goBackToRulesTableButton', { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/create_upgradeable_rules_payload.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/create_upgradeable_rules_payload.ts index b25320e1131e..7f9f66d1019c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/create_upgradeable_rules_payload.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/create_upgradeable_rules_payload.ts @@ -26,6 +26,7 @@ import { getValueForField } from './get_value_for_field'; interface CreateModifiedPrebuiltRuleAssetsProps { upgradeableRules: RuleTriad[]; requestBody: PerformRuleUpgradeRequestBody; + prebuiltRulesCustomizationEnabled: boolean; } interface ProcessedRules { @@ -36,9 +37,13 @@ interface ProcessedRules { export const createModifiedPrebuiltRuleAssets = ({ upgradeableRules, requestBody, + prebuiltRulesCustomizationEnabled, }: CreateModifiedPrebuiltRuleAssetsProps) => { return withSecuritySpanSync(createModifiedPrebuiltRuleAssets.name, () => { - const { pick_version: globalPickVersion = PickVersionValuesEnum.MERGED, mode } = requestBody; + const defaultPickVersion = prebuiltRulesCustomizationEnabled + ? PickVersionValuesEnum.MERGED + : PickVersionValuesEnum.TARGET; + const { pick_version: globalPickVersion = defaultPickVersion, mode } = requestBody; const { modifiedPrebuiltRuleAssets, processingErrors } = upgradeableRules.reduce( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts index 085c41db3a5d..c8b5d459f678 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts @@ -25,8 +25,12 @@ import { PREBUILT_RULES_OPERATION_SOCKET_TIMEOUT_MS } from '../../constants'; import { getUpgradeableRules } from './get_upgradeable_rules'; import { createModifiedPrebuiltRuleAssets } from './create_upgradeable_rules_payload'; import { getRuleGroups } from '../../model/rule_groups/get_rule_groups'; +import type { ConfigType } from '../../../../../config'; -export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => { +export const performRuleUpgradeRoute = ( + router: SecuritySolutionPluginRouter, + config: ConfigType +) => { router.versioned .post({ access: 'internal', @@ -75,10 +79,12 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => mode, }); + const { prebuiltRulesCustomizationEnabled } = config.experimentalFeatures; const { modifiedPrebuiltRuleAssets, processingErrors } = createModifiedPrebuiltRuleAssets( { upgradeableRules, requestBody: request.body, + prebuiltRulesCustomizationEnabled, } ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts index c9871f86a43e..2a2cf8823763 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts @@ -6,7 +6,7 @@ */ import type { SecuritySolutionPluginRouter } from '../../../../types'; - +import type { ConfigType } from '../../../../config'; import { getPrebuiltRulesAndTimelinesStatusRoute } from './get_prebuilt_rules_and_timelines_status/get_prebuilt_rules_and_timelines_status_route'; import { getPrebuiltRulesStatusRoute } from './get_prebuilt_rules_status/get_prebuilt_rules_status_route'; import { installPrebuiltRulesAndTimelinesRoute } from './install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route'; @@ -16,7 +16,10 @@ import { performRuleInstallationRoute } from './perform_rule_installation/perfor import { performRuleUpgradeRoute } from './perform_rule_upgrade/perform_rule_upgrade_route'; import { bootstrapPrebuiltRulesRoute } from './bootstrap_prebuilt_rules/bootstrap_prebuilt_rules'; -export const registerPrebuiltRulesRoutes = (router: SecuritySolutionPluginRouter) => { +export const registerPrebuiltRulesRoutes = ( + router: SecuritySolutionPluginRouter, + config: ConfigType +) => { // Legacy endpoints that we're going to deprecate getPrebuiltRulesAndTimelinesStatusRoute(router); installPrebuiltRulesAndTimelinesRoute(router); @@ -24,7 +27,7 @@ export const registerPrebuiltRulesRoutes = (router: SecuritySolutionPluginRouter // New endpoints for the rule upgrade and installation workflows getPrebuiltRulesStatusRoute(router); performRuleInstallationRoute(router); - performRuleUpgradeRoute(router); + performRuleUpgradeRoute(router, config); reviewRuleInstallationRoute(router); reviewRuleUpgradeRoute(router); bootstrapPrebuiltRulesRoute(router); diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 8fb74afc770b..f7d5ce7afb3d 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -81,7 +81,7 @@ export const initRoutes = ( ) => { registerFleetIntegrationsRoutes(router); registerLegacyRuleActionsRoutes(router, logger); - registerPrebuiltRulesRoutes(router); + registerPrebuiltRulesRoutes(router, config); registerRuleExceptionsRoutes(router); registerManageExceptionsRoutes(router); registerRuleManagementRoutes(router, config, ml, logger); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow_with_prebuilt_rule_customization.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow_with_prebuilt_rule_customization.cy.ts index 52b050f46c06..7b487b3d7fdb 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow_with_prebuilt_rule_customization.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/update_workflow_with_prebuilt_rule_customization.cy.ts @@ -5,23 +5,34 @@ * 2.0. */ -import { patchRule } from '../../../../tasks/api_calls/rules'; import { createRuleAssetSavedObject } from '../../../../helpers/rules'; import { MODIFIED_RULE_BADGE, + NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE, + RULES_UPDATES_TAB, RULES_UPDATES_TABLE, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, + UPGRADE_ALL_RULES_BUTTON, + UPGRADE_SELECTED_RULES_BUTTON, + getUpgradeSingleRuleButtonByRuleId, } from '../../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../../tasks/alerts_detection_rules'; import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; import { - installPrebuiltRuleAssets, createAndInstallMockedPrebuiltRules, + installPrebuiltRuleAssets, } from '../../../../tasks/api_calls/prebuilt_rules'; +import { patchRule } from '../../../../tasks/api_calls/rules'; import { resetRulesTableState } from '../../../../tasks/common'; import { login } from '../../../../tasks/login'; import { + assertRuleUpgradeConflictsModalShown, + assertRuleUpgradeSuccessToastShown, assertRulesNotPresentInRuleUpdatesTable, assertRulesPresentInRuleUpdatesTable, + assertUpgradeRequestIsComplete, clickRuleUpdatesTab, + clickUpgradeRuleWithoutConflicts, filterPrebuiltRulesUpdateTableByRuleCustomization, } from '../../../../tasks/prebuilt_rules'; import { visitRulesManagementTable } from '../../../../tasks/rules_management'; @@ -42,6 +53,90 @@ describe( }, () => { + describe('Upgrade of prebuilt rules without conflicts', () => { + const RULE_1_ID = 'rule_1'; + const RULE_2_ID = 'rule_2'; + const OUTDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Outdated rule 1', + rule_id: RULE_1_ID, + version: 1, + }); + const UPDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Updated rule 1', + rule_id: RULE_1_ID, + version: 2, + }); + const OUTDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Outdated rule 2', + rule_id: RULE_2_ID, + version: 1, + }); + const UPDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Updated rule 2', + rule_id: RULE_2_ID, + version: 2, + }); + beforeEach(() => { + login(); + resetRulesTableState(); + deleteAlertsAndRules(); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as( + 'updatePrebuiltRules' + ); + /* Create a new rule and install it */ + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); + /* Create a second version of the rule, making it available for update */ + installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); + + visitRulesManagementTable(); + clickRuleUpdatesTab(); + }); + + it('should upgrade prebuilt rules one by one', () => { + // Attempt to upgrade rule + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) + ).click(); + // Wait for request to complete + assertUpgradeRequestIsComplete([OUTDATED_RULE_1]); + + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1]); + }); + + it('should upgrade multiple selected prebuilt rules by selecting them individually', () => { + selectRulesByName([ + OUTDATED_RULE_1['security-rule'].name, + OUTDATED_RULE_2['security-rule'].name, + ]); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should upgrade multiple selected prebuilt rules by selecting all in page', () => { + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should upgrade all rules with available upgrades at once', () => { + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should display an empty screen when all rules with available updates have been upgraded', () => { + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + cy.get(RULES_UPDATES_TAB).should('not.exist'); + cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE).should('exist'); + }); + }); + describe('Upgrade of prebuilt rules with conflicts', () => { const RULE_1_ID = 'rule_1'; const RULE_2_ID = 'rule_2'; @@ -104,6 +199,60 @@ describe( // Verify only rules with non-customized rule sources are displayed assertRulesPresentInRuleUpdatesTable([OUTDATED_RULE_2]); cy.get(patchedName).should('not.exist'); + it('should upgrade prebuilt rules without conflicts one by one', () => { + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_2['security-rule'].rule_id) + ).click(); + // Wait for request to complete + assertUpgradeRequestIsComplete([OUTDATED_RULE_2]); + + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]); + }); + + it('should disable individual upgrade button for prebuilt rules with conflicts one by one', () => { + // Button should be disabled because of conflicts + expect( + cy + .get(getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id)) + .should('be.disabled') + ); + }); + + it('should warn about rules with conflicts not being updated when multiple rules are individually selected for update', () => { + selectRulesByName([patchedName, OUTDATED_RULE_2['security-rule'].name]); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertRuleUpgradeConflictsModalShown(); + clickUpgradeRuleWithoutConflicts(); + // Assert that only rules without conflicts are updated and the other remains in the table + assertUpgradeRequestIsComplete([OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]); + cy.get(RULES_UPDATES_TABLE).contains(patchedName); + }); + + it('should warn about rules with conflicts not being updated when all rules in page are selected', () => { + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertRuleUpgradeConflictsModalShown(); + clickUpgradeRuleWithoutConflicts(); + // Assert that only rules without conflicts are updated and the other remains in the table + assertUpgradeRequestIsComplete([OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]); + cy.get(RULES_UPDATES_TABLE).contains(patchedName); + }); + + it('should warn about rules with conflicts not being updated when all rules with available upgrades are upgraded at once', () => { + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + assertRuleUpgradeConflictsModalShown(); + clickUpgradeRuleWithoutConflicts(); + // Assert that only rules without conflicts are updated and the other remains in the table + assertUpgradeRequestIsComplete([OUTDATED_RULE_2]); + assertRuleUpgradeSuccessToastShown([OUTDATED_RULE_2]); + assertRulesNotPresentInRuleUpdatesTable([OUTDATED_RULE_2]); + cy.get(RULES_UPDATES_TABLE).contains(patchedName); + }); }); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_updates.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_updates.ts index 4b11a4624c3e..dbe907a9040f 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_updates.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_updates.ts @@ -10,3 +10,5 @@ export const RULE_UPGRADE_TABLE_MODIFICATION_FILTER_BUTTON = export const RULE_UPGRADE_TABLE_MODIFICATION_FILTER_PANEL = '[data-test-subj="rule-customization-filter-popover"]'; + +export const RULE_UPGRADE_CONFLICTS_MODAL = '[data-test-subj="upgradeConflictsModal"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts index d4148d5e632a..0dd833810f9b 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/prebuilt_rules.ts @@ -21,6 +21,7 @@ import { RULE_UPGRADE_TABLE_MODIFICATION_FILTER_BUTTON, RULE_UPGRADE_TABLE_MODIFICATION_FILTER_PANEL, } from '../screens/rule_updates'; +import { RULE_UPGRADE_CONFLICTS_MODAL } from '../screens/rule_updates'; export const clickAddElasticRulesButton = () => { cy.get(ADD_ELASTIC_RULES_BTN).click(); @@ -160,3 +161,11 @@ export const filterPrebuiltRulesUpdateTableByRuleCustomization = (text: string) cy.get(RULE_UPGRADE_TABLE_MODIFICATION_FILTER_PANEL).contains(text).click(); cy.get(RULE_UPGRADE_TABLE_MODIFICATION_FILTER_BUTTON).click(); }; + +export const assertRuleUpgradeConflictsModalShown = () => { + cy.get(RULE_UPGRADE_CONFLICTS_MODAL).should('be.visible'); +}; + +export const clickUpgradeRuleWithoutConflicts = () => { + cy.get(RULE_UPGRADE_CONFLICTS_MODAL).contains('button', 'Update').click(); +}; From 6fc010cf557af033cfbf5f904d75c8a493532dcb Mon Sep 17 00:00:00 2001 From: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:40:25 +0100 Subject: [PATCH 006/100] [TSVB] Remove `metrics:allowCheckingForFailedShards` Advanced Setting (#197227) ## Summary Removes the `metrics:allowCheckingForFailedShards` and doesn't allow to supress warnings about failed shards in TSVB. Fixes https://github.com/elastic/kibana/issues/193685 ### How I tested it 1. Create a TSVB visualization that includes 2024-10-22 day in the time picker, uses Kibana Ecommerce data view and uses a field `day_of_week_i`. (This data is only needed if you use the same test data in point 2) Screenshot 2024-10-23 at 12 28 21 1. On a different tab, edit the `kibana_sample_data_ecommerce` data view to include all the indices that start from `kibana_sample_data_ecommerce` (so change the pattern to `kibana_sample_data_ecommerce*`. Screenshot 2024-10-23 at 12 24 24 3. Add a new index with a mismatched mapping of a number field to a string in the dev console. ``` POST /kibana_sample_data_ecommerce_2/_doc { "order_date": "2024-10-22", "day_of_week_i": "Hello" } ``` Refresh the tab with TSVB visualization: Screenshot 2024-10-23 at 12 27 22 --- .../kbn-management/settings/setting_ids/index.ts | 1 - .../server/collectors/management/schema.ts | 4 ---- .../server/collectors/management/types.ts | 1 - src/plugins/telemetry/schema/oss_plugins.json | 6 ------ .../vis_types/timeseries/common/constants.ts | 1 - .../vis_types/timeseries/public/metrics_type.ts | 5 ++--- .../vis_types/timeseries/server/ui_settings.ts | 14 -------------- .../legacy/embeddable/visualize_embeddable.tsx | 4 ---- .../public/vis_types/base_vis_type.ts | 2 -- .../plugins/translations/translations/fr-FR.json | 2 -- .../plugins/translations/translations/ja-JP.json | 2 -- .../plugins/translations/translations/zh-CN.json | 2 -- 12 files changed, 2 insertions(+), 42 deletions(-) diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index b146be6f6e25..bc0f7206a283 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -34,7 +34,6 @@ export const HISTOGRAM_BAR_TARGET_ID = 'histogram:barTarget'; export const HISTOGRAM_MAX_BARS_ID = 'histogram:maxBars'; export const HISTORY_LIMIT_ID = 'history:limit'; export const META_FIELDS_ID = 'metaFields'; -export const METRICS_ALLOW_CHECKING_FOR_FAILED_SHARDS_ID = 'metrics:allowCheckingForFailedShards'; export const METRICS_ALLOW_STRING_INDICES_ID = 'metrics:allowStringIndices'; export const METRICS_MAX_BUCKETS_ID = 'metrics:max_buckets'; export const QUERY_ALLOW_LEADING_WILDCARDS_ID = 'query:allowLeadingWildcards'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 779150faa89f..e3374219b655 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -578,10 +578,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'metrics:allowCheckingForFailedShards': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'observability:apmDefaultServiceEnvironment': { type: 'keyword', _meta: { description: 'Default value of the setting was changed.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 2734ab630431..b49647c3d479 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -152,7 +152,6 @@ export interface UsageStats { 'discover:rowHeightOption': number; hideAnnouncements: boolean; isDefaultIndexMigrated: boolean; - 'metrics:allowCheckingForFailedShards': boolean; 'observability:syntheticsThrottlingEnabled': boolean; 'observability:enableLegacyUptimeApp': boolean; 'observability:apmLabsButton': boolean; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index d54a75b313cd..aed47fa83964 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10719,12 +10719,6 @@ "description": "Non-default value of setting." } }, - "metrics:allowCheckingForFailedShards": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "observability:apmDefaultServiceEnvironment": { "type": "keyword", "_meta": { diff --git a/src/plugins/vis_types/timeseries/common/constants.ts b/src/plugins/vis_types/timeseries/common/constants.ts index e881fb767f0d..4734d25d191c 100644 --- a/src/plugins/vis_types/timeseries/common/constants.ts +++ b/src/plugins/vis_types/timeseries/common/constants.ts @@ -10,7 +10,6 @@ export const UI_SETTINGS = { MAX_BUCKETS_SETTING: 'metrics:max_buckets', ALLOW_STRING_INDICES: 'metrics:allowStringIndices', - ALLOW_CHECKING_FOR_FAILED_SHARDS: 'metrics:allowCheckingForFailedShards', }; export const SERIES_SEPARATOR = '╰┄►'; export const INDEXES_SEPARATOR = ','; diff --git a/src/plugins/vis_types/timeseries/public/metrics_type.ts b/src/plugins/vis_types/timeseries/public/metrics_type.ts index eec28bb6cf47..65150ab9eabe 100644 --- a/src/plugins/vis_types/timeseries/public/metrics_type.ts +++ b/src/plugins/vis_types/timeseries/public/metrics_type.ts @@ -24,9 +24,9 @@ import { extractIndexPatternValues, isStringTypeIndexPattern, } from '../common/index_patterns_utils'; -import { TSVB_DEFAULT_COLOR, UI_SETTINGS, VIS_TYPE } from '../common/constants'; +import { TSVB_DEFAULT_COLOR, VIS_TYPE } from '../common/constants'; import { toExpressionAst } from './to_ast'; -import { getDataViewsStart, getUISettings } from './services'; +import { getDataViewsStart } from './services'; import type { TimeseriesVisDefaultParams, TimeseriesVisParams } from './types'; import type { IndexPatternValue, Panel } from '../common/types'; @@ -188,6 +188,5 @@ export const metricsVisDefinition: VisTypeDefinition< requests: new RequestAdapter(), }), requiresSearch: true, - suppressWarnings: () => !getUISettings().get(UI_SETTINGS.ALLOW_CHECKING_FOR_FAILED_SHARDS), getUsedIndexPattern: getUsedIndexPatterns, }; diff --git a/src/plugins/vis_types/timeseries/server/ui_settings.ts b/src/plugins/vis_types/timeseries/server/ui_settings.ts index 0d3dcc681110..9d6ac0f0856d 100644 --- a/src/plugins/vis_types/timeseries/server/ui_settings.ts +++ b/src/plugins/vis_types/timeseries/server/ui_settings.ts @@ -38,18 +38,4 @@ export const getUiSettings: () => Record = () => ({ }), schema: schema.boolean(), }, - [UI_SETTINGS.ALLOW_CHECKING_FOR_FAILED_SHARDS]: { - name: i18n.translate('visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsTitle', { - defaultMessage: 'Show TSVB request shard failures', - }), - value: true, - description: i18n.translate( - 'visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsText', - { - defaultMessage: - 'Show warning message for partial data in TSVB charts if the request succeeds for some shards but fails for others.', - } - ), - schema: schema.boolean(), - }, }); diff --git a/src/plugins/visualizations/public/legacy/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/legacy/embeddable/visualize_embeddable.tsx index 4f6bfa344a0a..196753d73b28 100644 --- a/src/plugins/visualizations/public/legacy/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/legacy/embeddable/visualize_embeddable.tsx @@ -353,10 +353,6 @@ export class VisualizeEmbeddable ); return true; } - if (this.vis.type.suppressWarnings?.()) { - // if the vis type wishes to supress all warnings, return true so the default logic won't pick it up - return true; - } }); } diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts index f3a88245008e..e7519729eaa0 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -43,7 +43,6 @@ export class BaseVisType { public readonly disableCreate; public readonly disableEdit; public readonly requiresSearch; - public readonly suppressWarnings; public readonly hasPartialRows; public readonly hierarchicalData; public readonly setup; @@ -70,7 +69,6 @@ export class BaseVisType { this.icon = opts.icon; this.image = opts.image; this.order = opts.order ?? 0; - this.suppressWarnings = opts.suppressWarnings; this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} }); this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} }); this.options = defaultsDeep({}, opts.options, defaultOptions); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 986e2758f948..0008cac9a55d 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -9090,8 +9090,6 @@ "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "Supprimer", "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "Réactiver", "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "Désactiver temporairement", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsText": "Afficher un message d'avertissement pour les données partielles dans les graphiques TSVB si la requête réussit pour certaines partitions, mais échoue pour d'autres.", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsTitle": "Afficher les échecs de partition de requête TSVB", "visTypeTimeseries.advancedSettings.allowStringIndicesText": "Vous permet d'interroger les index Elasticsearch dans les visualisations TSVB.", "visTypeTimeseries.advancedSettings.allowStringIndicesTitle": "Autoriser les index de chaîne dans TSVB", "visTypeTimeseries.advancedSettings.maxBucketsText": "A un impact sur la densité de l'histogramme TSVB. Doit être défini sur une valeur supérieure à \"histogram:maxBars\".", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 58e6643f0ca5..1d90ec5e84fd 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9079,8 +9079,6 @@ "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "削除", "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "再度有効にする", "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "一時的に無効にする", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsText": "一部のシャードでリクエストが成功し、その他のシャードで失敗した場合に、TSVBグラフの部分データに関する警告メッセージを表示します。", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsTitle": "TSVBリクエストシャード失敗", "visTypeTimeseries.advancedSettings.allowStringIndicesText": "TSVBビジュアライゼーションでElasticsearchインデックスをクエリーできます。", "visTypeTimeseries.advancedSettings.allowStringIndicesTitle": "TSVBで文字列インデックスを許可", "visTypeTimeseries.advancedSettings.maxBucketsText": "TSVBヒストグラム密度に影響します。「histogram:maxBars」よりも大きく設定する必要があります。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 598339631a84..30eb1f0c0fc2 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8902,8 +8902,6 @@ "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "删除", "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "重新启用", "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "暂时禁用", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsText": "如果请求对某些分片成功,但对其他分片失败,将对 TSVB 图表中的部分数据显示警告消息。", - "visTypeTimeseries.advancedSettings.allowCheckingForFailedShardsTitle": "显示 TSVB 请求分片失败", "visTypeTimeseries.advancedSettings.allowStringIndicesText": "允许您在 TSVB 可视化中查询 Elasticsearch 索引。", "visTypeTimeseries.advancedSettings.allowStringIndicesTitle": "TSVB 中允许字符串索引", "visTypeTimeseries.advancedSettings.maxBucketsText": "影响 TSVB 直方图密度。必须设置为高于'histogram:maxBars'。", From 8136bcc0e43a682dfa4484f112a287210936f817 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 11 Nov 2024 14:00:08 +0100 Subject: [PATCH 007/100] [Discover] Make use of stateContainer action onDataViewEdited (#199588) Duplicating code that's triggered when a dataview is being edited --- .../components/top_nav/discover_topnav.tsx | 40 ++++--------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 20ab07e1155c..26f056321553 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -8,8 +8,8 @@ */ import React, { useCallback, useEffect, useMemo, useRef } from 'react'; -import { type DataView, DataViewType } from '@kbn/data-views-plugin/public'; -import { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; +import { DataViewType } from '@kbn/data-views-plugin/public'; +import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; import { ENABLE_ESQL } from '@kbn/esql-utils'; import { TextBasedLanguages } from '@kbn/esql-utils'; import { DiscoverFlyouts, dismissAllFlyoutsExceptFor } from '@kbn/discover-utils'; @@ -20,7 +20,6 @@ import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; import { onSaveSearch } from './on_save_search'; import { useDiscoverCustomization } from '../../../../customizations'; -import { addLog } from '../../../../utils/add_log'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import { useDiscoverTopNav } from './use_discover_topnav'; import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; @@ -47,15 +46,8 @@ export const DiscoverTopNav = ({ onCancelClick, }: DiscoverTopNavProps) => { const services = useDiscoverServices(); - const { - dataViewEditor, - navigation, - dataViewFieldEditor, - data, - uiSettings, - dataViews, - setHeaderActionMenu, - } = services; + const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, setHeaderActionMenu } = + services; const query = useAppStateSelector((state) => state.query); const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews); const dataView = useInternalStateSelector((state) => state.dataView!); @@ -93,7 +85,7 @@ export const DiscoverTopNav = ({ const editField = useMemo( () => canEditDataView - ? async (fieldName?: string, uiAction: 'edit' | 'add' = 'edit') => { + ? async (fieldName?: string) => { if (dataView?.id) { const dataViewInstance = await data.dataViews.get(dataView.id); closeFieldEditor.current = await dataViewFieldEditor.openEditor({ @@ -112,7 +104,7 @@ export const DiscoverTopNav = ({ ); const addField = useMemo( - () => (canEditDataView && editField ? () => editField(undefined, 'add') : undefined), + () => (canEditDataView && editField ? () => editField() : undefined), [editField, canEditDataView] ); @@ -123,23 +115,6 @@ export const DiscoverTopNav = ({ }); }, [dataViewEditor, stateContainer]); - const onEditDataView = useCallback( - async (editedDataView: DataView) => { - if (editedDataView.isPersisted()) { - // Clear the current data view from the cache and create a new instance - // of it, ensuring we have a new object reference to trigger a re-render - dataViews.clearInstanceCache(editedDataView.id); - stateContainer.actions.setDataView(await dataViews.create(editedDataView.toSpec(), true)); - } else { - await stateContainer.actions.updateAdHocDataViewId(); - } - stateContainer.actions.loadDataViewList(); - addLog('[DiscoverTopNav] onEditDataView triggers data fetching'); - stateContainer.dataState.fetch(); - }, - [dataViews, stateContainer.actions, stateContainer.dataState] - ); - const updateSavedQueryId = (newSavedQueryId: string | undefined) => { const { appState } = stateContainer; if (newSavedQueryId) { @@ -223,14 +198,13 @@ export const DiscoverTopNav = ({ textBasedLanguages: supportedTextBasedLanguages, adHocDataViews, savedDataViews, - onEditDataView, + onEditDataView: stateContainer.actions.onDataViewEdited, }; }, [ adHocDataViews, addField, createNewDataView, dataView, - onEditDataView, savedDataViews, stateContainer, uiSettings, From a4db534e79f04502c1a73ff9905337520d9950b0 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 11 Nov 2024 07:17:56 -0600 Subject: [PATCH 008/100] [ci] Fix environment for cache update (#199644) Switches to setting properties --- .../steps/es_serverless/promote_es_serverless_image.sh | 4 ++-- .buildkite/scripts/steps/es_snapshots/promote.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh b/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh index f1c7de0f8139..4598e03b1cdb 100755 --- a/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh +++ b/.buildkite/scripts/steps/es_serverless/promote_es_serverless_image.sh @@ -77,6 +77,6 @@ steps: async: true build: env: - IMAGES_CONFIG="kibana/images.yml" - RETRY="1" + IMAGES_CONFIG: "kibana/images.yml" + RETRY: "1" EOF diff --git a/.buildkite/scripts/steps/es_snapshots/promote.sh b/.buildkite/scripts/steps/es_snapshots/promote.sh index cb717ac5c922..d09ef2d2420a 100755 --- a/.buildkite/scripts/steps/es_snapshots/promote.sh +++ b/.buildkite/scripts/steps/es_snapshots/promote.sh @@ -23,7 +23,7 @@ steps: async: true build: env: - IMAGES_CONFIG="kibana/images.yml" - RETRY="1" + IMAGES_CONFIG: "kibana/images.yml" + RETRY: "1" EOF fi From c050bb4175aeaad97b8b4d1a76abdb7b0f22e003 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 11 Nov 2024 07:36:48 -0600 Subject: [PATCH 009/100] [ci] Skip intermediate builds (#199540) Removes intermediate builds from all pipelines except on-merge or pipelines triggered from on-merge. https://registry.terraform.io/providers/buildkite/buildkite/1.0.0-docs/docs/resources/pipeline#skip_intermediate_builds-1 > (Boolean) Whether to skip queued builds if a new commit is pushed to a matching branch. e.g. push several commits to a pull request before the first build has started and CI will run for each commit. --- .../pipeline-resource-definitions/_templates/_new_pipeline.yml | 1 - .buildkite/pipeline-resource-definitions/kibana-api-docs.yml | 1 - .../kibana-apis-capacity-testing-daily.yml | 1 - .../kibana-artifacts-snapshot.yml | 1 - .../pipeline-resource-definitions/kibana-artifacts-staging.yml | 1 - .../pipeline-resource-definitions/kibana-artifacts-trigger.yml | 1 - .../kibana-chrome-forward-testing.yml | 1 - .../pipeline-resource-definitions/kibana-coverage-daily.yml | 1 - .../pipeline-resource-definitions/kibana-deploy-project.yml | 1 - .../kibana-es-forward-testing.yml | 1 - .../kibana-es-serverless-snapshots.yml | 1 - .../pipeline-resource-definitions/kibana-es-snapshots.yml | 3 --- .../pipeline-resource-definitions/kibana-esql-grammar-sync.yml | 1 - .buildkite/pipeline-resource-definitions/kibana-flaky.yml | 1 - .../kibana-fleet-packages-daily.yml | 1 - .../pipeline-resource-definitions/kibana-performance-daily.yml | 1 - .buildkite/pipeline-resource-definitions/kibana-pr.yml | 1 - .../kibana-purge-cloud-deployments.yml | 1 - .../kibana-serverless-release-testing.yml | 1 - .../kibana-serverless-release.yml | 1 - .buildkite/pipeline-resource-definitions/kibana-vm-images.yml | 1 - .../scalability_testing-daily.yml | 1 - ...verless-security-solution-quality-gate-defend-workflows.yml | 1 - ...verless-security-solution-quality-gate-detection-engine.yml | 1 - ...verless-security-solution-quality-gate-entity-analytics.yml | 1 - ...ibana-serverless-security-solution-quality-gate-explore.yml | 1 - ...kibana-serverless-security-solution-quality-gate-gen-ai.yml | 1 - ...erverless-security-solution-quality-gate-investigations.yml | 1 - ...rverless-security-solution-quality-gate-rule-management.yml | 1 - .../trigger-version-dependent-jobs.yml | 1 - 30 files changed, 32 deletions(-) diff --git a/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml b/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml index 6ef0d7652b96..877316ecdfd8 100644 --- a/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml +++ b/.buildkite/pipeline-resource-definitions/_templates/_new_pipeline.yml @@ -44,7 +44,6 @@ spec: repository: elastic/kibana # Point to a pipeline implementation, detailing the pipeline steps to run pipeline_file: .buildkite/pipelines/your-pipeline-name.yml - skip_intermediate_builds: false provider_settings: prefix_pull_request_fork_branch_names: false skip_pull_request_builds_for_existing_commits: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml b/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml index 26ff7242dac6..168e24b01eaa 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-api-docs.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/build_api_docs.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml index 244a0351de0b..787534984f63 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-apis-capacity-testing-daily.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/scalability/api_capacity_testing_daily.yml - skip_intermediate_builds: false provider_settings: trigger_mode: none build_branches: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml index f994f0cba33c..8d417739ef93 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-snapshot.yml @@ -25,7 +25,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml index 1d7b0488c7b3..28d4326cce38 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-staging.yml @@ -25,7 +25,6 @@ spec: allow_rebuilds: true repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml index f08e505b9aab..51776700abf7 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-artifacts-trigger.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/artifacts_trigger.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml index 15265da35f39..b7c7c83f828d 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-chrome-forward-testing.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/chrome_forward_testing.yml - skip_intermediate_builds: true provider_settings: prefix_pull_request_fork_branch_names: false skip_pull_request_builds_for_existing_commits: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml index 4192bb918658..67182739ddde 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-coverage-daily.yml @@ -29,7 +29,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/code_coverage/daily.yml - skip_intermediate_builds: false provider_settings: prefix_pull_request_fork_branch_names: false skip_pull_request_builds_for_existing_commits: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-deploy-project.yml b/.buildkite/pipeline-resource-definitions/kibana-deploy-project.yml index 490c9d9afc4e..f098ff82f322 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-deploy-project.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-deploy-project.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/serverless_deployment/project-build-and-deploy-pr.yml - skip_intermediate_builds: true provider_settings: build_pull_requests: true prefix_pull_request_fork_branch_names: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml index fa4ee2d26387..27549d37a66a 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-forward-testing.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_forward.yml # Note: this file exists in 7.17 only - skip_intermediate_builds: false provider_settings: prefix_pull_request_fork_branch_names: false trigger_mode: none diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml index 6ba182ccd393..de60f5311669 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-serverless-snapshots.yml @@ -28,7 +28,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_serverless/verify_es_serverless_image.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml index b99fd82408b7..652ce7b35c30 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-es-snapshots.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_snapshots/build.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false @@ -96,7 +95,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_snapshots/promote.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false @@ -146,7 +144,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_snapshots/verify.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml b/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml index 8cc4b54a5ce0..d2259c7cf769 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-esql-grammar-sync.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/esql_grammar_sync.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml index f1c348e05920..e4a840fd89d7 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-flaky.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-flaky.yml @@ -22,7 +22,6 @@ spec: default_branch: refs/pull/INSERT_PR_NUMBER/head repository: elastic/kibana pipeline_file: .buildkite/pipelines/flaky_tests/pipeline.sh - skip_intermediate_builds: false provider_settings: build_branches: true build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml index d948460513c8..cb8e0143cb24 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-fleet-packages-daily.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/fleet/packages_daily.yml - skip_intermediate_builds: false provider_settings: trigger_mode: none publish_commit_status: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml index 915cce93a248..166b8119edd6 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-performance-daily.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/performance/daily.yml - skip_intermediate_builds: false provider_settings: trigger_mode: none build_branches: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-pr.yml b/.buildkite/pipeline-resource-definitions/kibana-pr.yml index 0f5246106ac4..19f05ef012b0 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-pr.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-pr.yml @@ -29,7 +29,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/scripts/pipelines/pull_request/pipeline.sh - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: true diff --git a/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml b/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml index 9124d001d6f7..d7d35b8f8d5d 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-purge-cloud-deployments.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/purge_cloud_deployments.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml index 5276871fa1c9..84c15018933a 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-release-testing.yml @@ -26,7 +26,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/es_serverless/emergency_release_branch_testing.yml - skip_intermediate_builds: false provider_settings: build_branches: true build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml b/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml index e1457f10420f..568ed8ba2b16 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-serverless-release.yml @@ -23,7 +23,6 @@ spec: ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' default_branch: main allow_rebuilds: false - skip_intermediate_builds: false repository: elastic/kibana pipeline_file: .buildkite/pipelines/serverless_deployment/run_serverless_release_assistant.yml provider_settings: diff --git a/.buildkite/pipeline-resource-definitions/kibana-vm-images.yml b/.buildkite/pipeline-resource-definitions/kibana-vm-images.yml index dd8a6c945c45..c0ca615646e9 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-vm-images.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-vm-images.yml @@ -24,7 +24,6 @@ spec: default_branch: main repository: elastic/ci-agent-images pipeline_file: vm-images/.buildkite/pipeline.yml - skip_intermediate_builds: false provider_settings: trigger_mode: none schedules: diff --git a/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml b/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml index 162bb6220ea1..48dfd711cec7 100644 --- a/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml +++ b/.buildkite/pipeline-resource-definitions/scalability_testing-daily.yml @@ -27,7 +27,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/pipelines/scalability/daily.yml - skip_intermediate_builds: false provider_settings: trigger_mode: none build_branches: true diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml index d4d2541f1c4a..3aa82698ee52 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-defend-workflows.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_defend_workflows.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml index 77361eed441e..4d8265664d0d 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-detection-engine.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_detection_engine.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml index 49338bf7b6d3..9297f4c6411f 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-entity-analytics.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_entity_analytics.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml index ee8cf00a755f..1ac5f46d5610 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-explore.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_explore.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml index f22e321176eb..7bf3f50d5017 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-gen-ai.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_gen_ai.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml index 7de3b5f8cc28..dd16077339d9 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-investigations.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_investigations.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml index 4f095294f442..ee9147c90563 100644 --- a/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml +++ b/.buildkite/pipeline-resource-definitions/security-solution-quality-gate/kibana-serverless-security-solution-quality-gate-rule-management.yml @@ -17,7 +17,6 @@ spec: spec: repository: elastic/kibana pipeline_file: .buildkite/pipelines/security_solution_quality_gate/mki_security_solution_rule_management.yml - skip_intermediate_builds: false provider_settings: build_branches: false build_pull_requests: false diff --git a/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml index 8dd486c3176c..dce527f9e7f6 100644 --- a/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml +++ b/.buildkite/pipeline-resource-definitions/trigger-version-dependent-jobs.yml @@ -31,7 +31,6 @@ spec: default_branch: main repository: elastic/kibana pipeline_file: .buildkite/scripts/pipelines/trigger_version_dependent_jobs/pipeline.sh - skip_intermediate_builds: false provider_settings: prefix_pull_request_fork_branch_names: false skip_pull_request_builds_for_existing_commits: true From f77a8052f7a3e39cb3b61d1b61dbef8b6c254525 Mon Sep 17 00:00:00 2001 From: Katerina Date: Mon, 11 Nov 2024 15:49:54 +0200 Subject: [PATCH 010/100] [APM] Migrate `/mobile` API tests to deployment agnostic folder (#199021) closes https://github.com/elastic/kibana/issues/198980 In addition to migrating the mobile api tests, the PR includes - Fixing mapping issue with `error.grouping_name` which causing to drop documents - Fix and unskip mobile tests ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` TODO - [x] flaky runner - [x] locally pass - [x] mki run --------- Co-authored-by: Carlos Crespo Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../src/lib/apm/instance.ts | 1 - .../src/lib/apm/mobile_device.ts | 1 - .../latest_agent_versions.spec.ts | 2 - .../apis/observability/apm/index.ts | 1 + .../mobile/crashes/crash_group_list.spec.ts | 160 ++++++++++++++ .../apm/mobile/crashes/distribution.spec.ts | 209 ++++++++++++++++++ .../apm}/mobile/crashes/generate_data.ts | 0 .../apm}/mobile/errors/generate_data.ts | 0 .../mobile/errors/group_id_samples.spec.ts | 191 ++++++++++++++++ .../apm}/mobile/generate_mobile_data.ts | 0 .../apis/observability/apm/mobile/index.ts | 25 +++ ...obile_detailed_statistics_by_field.spec.ts | 53 +++-- .../apm}/mobile/mobile_filters.spec.ts | 40 ++-- .../mobile_http_requests_timeseries.spec.ts | 88 ++++---- .../apm}/mobile/mobile_location_stats.spec.ts | 184 +++++++-------- .../mobile_main_statistics_by_field.spec.ts | 139 ++++++------ .../apm/mobile/mobile_most_used_chart.spec.ts | 104 +++++++++ .../mobile/mobile_sessions_timeseries.spec.ts | 133 +++++++++++ .../apm}/mobile/mobile_stats.spec.ts | 188 ++++++++-------- .../apm}/mobile/mobile_terms_by_field.spec.ts | 118 +++++----- .../mobile/crashes/crash_group_list.spec.ts | 157 ------------- .../tests/mobile/crashes/distribution.spec.ts | 203 ----------------- .../mobile/errors/group_id_samples.spec.ts | 189 ---------------- .../mobile/mobile_most_used_chart.spec.ts | 105 --------- .../mobile/mobile_sessions_timeseries.spec.ts | 128 ----------- x-pack/test/tsconfig.json | 2 +- 26 files changed, 1223 insertions(+), 1198 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/crash_group_list.spec.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/distribution.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/crashes/generate_data.ts (100%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/errors/generate_data.ts (100%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/group_id_samples.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/generate_mobile_data.ts (100%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_detailed_statistics_by_field.spec.ts (78%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_filters.spec.ts (88%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_http_requests_timeseries.spec.ts (58%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_location_stats.spec.ts (60%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_main_statistics_by_field.spec.ts (59%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_most_used_chart.spec.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_sessions_timeseries.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_stats.spec.ts (56%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/mobile/mobile_terms_by_field.spec.ts (70%) delete mode 100644 x-pack/test/apm_api_integration/tests/mobile/crashes/crash_group_list.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/mobile/crashes/distribution.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/mobile/errors/group_id_samples.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts index 31c301591ae4..d3e393b68639 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts @@ -70,7 +70,6 @@ export class Instance extends Entity { ...this.fields, 'error.type': 'crash', 'error.exception': [{ message, ...(type ? { type } : {}) }], - 'error.grouping_name': getErrorGroupingKey(message), }); } error({ diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts index b4dfafe22fc4..c0947f75531a 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/mobile_device.ts @@ -262,7 +262,6 @@ export class MobileDevice extends Entity { 'error.type': 'crash', 'error.id': generateLongIdWithSeed(message), 'error.exception': [{ message, ...{ type: 'crash' } }], - 'error.grouping_name': groupingName || message, }); } } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts index 7d4099388525..acc31a3743da 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts @@ -23,9 +23,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon it('returns a version when agent is listed in the file', async () => { const { status, body } = await callApi(); expect(status).to.be(200); - const agents = body.data; - const nodeAgent = agents[nodeAgentName] as ElasticApmAgentLatestVersion; expect(nodeAgent?.latest_version).not.to.be(undefined); }); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index abc7f72945e2..93ef6ed2764f 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -12,6 +12,7 @@ export default function apmApiIntegrationTests({ }: DeploymentAgnosticFtrProviderContext) { describe('APM', function () { loadTestFile(require.resolve('./agent_explorer')); + loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); }); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/crash_group_list.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/crash_group_list.spec.ts new file mode 100644 index 000000000000..1a053055617a --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/crash_group_list.spec.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { + APIClientRequestParamsOf, + APIReturnType, +} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context'; + +type ErrorGroups = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics'>['errorGroups']; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + const serviceName = 'synth-swift'; + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + async function callApi( + overrides?: RecursivePartial< + APIClientRequestParamsOf<'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics'>['params'] + > + ) { + return await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics', + params: { + path: { serviceName, ...overrides?.path }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + ...overrides?.query, + }, + }, + }); + } + + describe('Crash group list', () => { + it('handles empty state', async () => { + const response = await callApi(); + expect(response.status).to.be(200); + expect(response.body.errorGroups).to.empty(); + }); + + describe('when data is loaded', () => { + describe('errors group', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + const appleTransaction = { + name: 'GET /apple 🍎 ', + successRate: 75, + failureRate: 25, + }; + + const bananaTransaction = { + name: 'GET /banana 🍌', + successRate: 50, + failureRate: 50, + }; + + before(async () => { + const serviceInstance = apm + .service({ name: serviceName, environment: 'production', agentName: 'swift' }) + .instance('instance-a'); + + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await apmSynthtraceEsClient.index([ + timerange(start, end) + .interval('1m') + .rate(appleTransaction.successRate) + .generator((timestamp) => + serviceInstance + .transaction({ transactionName: appleTransaction.name }) + .timestamp(timestamp) + .duration(1000) + .success() + ), + timerange(start, end) + .interval('1m') + .rate(appleTransaction.failureRate) + .generator((timestamp) => + serviceInstance + .transaction({ transactionName: appleTransaction.name }) + .errors( + serviceInstance + .crash({ + message: 'crash 1', + }) + .timestamp(timestamp) + ) + .duration(1000) + .timestamp(timestamp) + .failure() + ), + timerange(start, end) + .interval('1m') + .rate(bananaTransaction.successRate) + .generator((timestamp) => + serviceInstance + .transaction({ transactionName: bananaTransaction.name }) + .timestamp(timestamp) + .duration(1000) + .success() + ), + timerange(start, end) + .interval('1m') + .rate(bananaTransaction.failureRate) + .generator((timestamp) => + serviceInstance + .transaction({ transactionName: bananaTransaction.name }) + .errors( + serviceInstance + .crash({ + message: 'crash 2', + }) + .timestamp(timestamp) + ) + .duration(1000) + .timestamp(timestamp) + .failure() + ), + ]); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('returns the correct data', () => { + let errorGroups: ErrorGroups; + before(async () => { + const response = await callApi(); + errorGroups = response.body.errorGroups; + }); + it('returns correct number of crashes', () => { + expect(errorGroups.length).to.equal(2); + expect(errorGroups.map((error) => error.name).sort()).to.eql(['crash 1', 'crash 2']); + }); + + it('returns correct occurrences', () => { + const numberOfBuckets = 15; + expect(errorGroups.map((error) => error.occurrences).sort()).to.eql([ + appleTransaction.failureRate * numberOfBuckets, + bananaTransaction.failureRate * numberOfBuckets, + ]); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/distribution.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/distribution.spec.ts new file mode 100644 index 000000000000..b726f9df3349 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/distribution.spec.ts @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { first, last, sumBy } from 'lodash'; +import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; +import { + APIClientRequestParamsOf, + APIReturnType, +} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; + +import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context'; +import { config, generateData } from './generate_data'; + +type ErrorsDistribution = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution'>; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + const serviceName = 'synth-swift'; + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + async function callApi( + overrides?: RecursivePartial< + APIClientRequestParamsOf<'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution'>['params'] + > + ) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution', + params: { + path: { + serviceName, + ...overrides?.path, + }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + ...overrides?.query, + }, + }, + }); + return response; + } + + describe('Distribution', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + }); + + it('handles the empty state', async () => { + const response = await callApi(); + expect(response.status).to.be(200); + expect(response.body.currentPeriod.length).to.be(0); + expect(response.body.previousPeriod.length).to.be(0); + }); + + describe('when data is loaded', () => { + describe('errors distribution', () => { + const { appleTransaction, bananaTransaction } = config; + before(async () => { + await generateData({ serviceName, start, end, apmSynthtraceEsClient }); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('without comparison', () => { + let errorsDistribution: ErrorsDistribution; + before(async () => { + const response = await callApi(); + errorsDistribution = response.body; + }); + + it('displays combined number of occurrences', () => { + const countSum = sumBy(errorsDistribution.currentPeriod, 'y'); + const numberOfBuckets = 15; + expect(countSum).to.equal( + (appleTransaction.failureRate + bananaTransaction.failureRate) * numberOfBuckets + ); + }); + + describe('displays correct start in errors distribution chart', () => { + let errorsDistributionWithComparison: ErrorsDistribution; + before(async () => { + const responseWithComparison = await callApi({ + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + offset: '15m', + }, + }); + errorsDistributionWithComparison = responseWithComparison.body; + }); + it('has same start time when comparison is enabled', () => { + expect(first(errorsDistribution.currentPeriod)?.x).to.equal( + first(errorsDistributionWithComparison.currentPeriod)?.x + ); + }); + }); + }); + + describe('displays occurrences for type "apple transaction" only', () => { + let errorsDistribution: ErrorsDistribution; + before(async () => { + const response = await callApi({ + query: { kuery: `error.exception.type:"${appleTransaction.name}"` }, + }); + errorsDistribution = response.body; + }); + it('displays combined number of occurrences', () => { + const countSum = sumBy(errorsDistribution.currentPeriod, 'y'); + const numberOfBuckets = 15; + expect(countSum).to.equal(appleTransaction.failureRate * numberOfBuckets); + }); + }); + + describe('with comparison', () => { + describe('when data is returned', () => { + let errorsDistribution: ErrorsDistribution; + before(async () => { + const fiveMinutes = 5 * 60 * 1000; + const response = await callApi({ + query: { + start: new Date(end - fiveMinutes).toISOString(), + end: new Date(end).toISOString(), + offset: '5m', + }, + }); + errorsDistribution = response.body; + }); + it('returns some data', () => { + const hasCurrentPeriodData = errorsDistribution.currentPeriod.some(({ y }) => + isFiniteNumber(y) + ); + + const hasPreviousPeriodData = errorsDistribution.previousPeriod.some(({ y }) => + isFiniteNumber(y) + ); + + expect(hasCurrentPeriodData).to.equal(true); + expect(hasPreviousPeriodData).to.equal(true); + }); + + it('has same start time for both periods', () => { + expect(first(errorsDistribution.currentPeriod)?.x).to.equal( + first(errorsDistribution.previousPeriod)?.x + ); + }); + + it('has same end time for both periods', () => { + expect(last(errorsDistribution.currentPeriod)?.x).to.equal( + last(errorsDistribution.previousPeriod)?.x + ); + }); + + it('returns same number of buckets for both periods', () => { + expect(errorsDistribution.currentPeriod.length).to.equal( + errorsDistribution.previousPeriod.length + ); + }); + }); + + describe('when no data is returned', () => { + let errorsDistribution: ErrorsDistribution; + before(async () => { + const response = await callApi({ + query: { + start: '2021-01-03T00:00:00.000Z', + end: '2021-01-03T00:15:00.000Z', + offset: '1d', + }, + }); + errorsDistribution = response.body; + }); + + it('has same start time for both periods', () => { + expect(first(errorsDistribution.currentPeriod)?.x).to.equal( + first(errorsDistribution.previousPeriod)?.x + ); + }); + + it('has same end time for both periods', () => { + expect(last(errorsDistribution.currentPeriod)?.x).to.equal( + last(errorsDistribution.previousPeriod)?.x + ); + }); + + it('returns same number of buckets for both periods', () => { + expect(errorsDistribution.currentPeriod.length).to.equal( + errorsDistribution.previousPeriod.length + ); + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/crashes/generate_data.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/generate_data.ts similarity index 100% rename from x-pack/test/apm_api_integration/tests/mobile/crashes/generate_data.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/crashes/generate_data.ts diff --git a/x-pack/test/apm_api_integration/tests/mobile/errors/generate_data.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/generate_data.ts similarity index 100% rename from x-pack/test/apm_api_integration/tests/mobile/errors/generate_data.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/generate_data.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/group_id_samples.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/group_id_samples.spec.ts new file mode 100644 index 000000000000..6e5a19327c24 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/errors/group_id_samples.spec.ts @@ -0,0 +1,191 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { timerange } from '@kbn/apm-synthtrace-client'; +import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service'; +import { orderBy } from 'lodash'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../../ftr_provider_context'; +import { config, generateData } from './generate_data'; + +type ErrorGroupSamples = + APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>; + +type ErrorSampleDetails = + APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}'>; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + const serviceName = 'synth-go'; + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + async function callErrorGroupSamplesApi({ groupId }: { groupId: string }) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', + params: { + path: { + serviceName, + groupId, + }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + }, + }, + }); + return response; + } + + async function callErrorSampleDetailsApi(errorId: string) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}', + params: { + path: { + serviceName, + groupId: 'foo', + errorId, + }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + }, + }, + }); + return response; + } + + describe('Group id samples', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + }); + + it('handles the empty state', async () => { + const response = await callErrorGroupSamplesApi({ groupId: 'foo' }); + expect(response.status).to.be(200); + expect(response.body.occurrencesCount).to.be(0); + }); + + describe('when samples data is loaded', () => { + let errorsSamplesResponse: ErrorGroupSamples; + const { bananaTransaction } = config; + describe('error group id', () => { + before(async () => { + await generateData({ serviceName, start, end, apmSynthtraceEsClient }); + const response = await callErrorGroupSamplesApi({ + groupId: '0000000000000000000000000Error 1', + }); + errorsSamplesResponse = response.body; + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('displays correct number of occurrences', () => { + const numberOfBuckets = 15; + expect(errorsSamplesResponse.occurrencesCount).to.equal( + bananaTransaction.failureRate * numberOfBuckets + ); + }); + }); + }); + + // github.com/elastic/kibana/issues/177665 + describe('when error sample data is loaded', () => { + describe('error sample id', () => { + before(async () => { + await generateData({ serviceName, start, end, apmSynthtraceEsClient }); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('return correct data', () => { + let errorSampleDetailsResponse: ErrorSampleDetails; + before(async () => { + const errorsSamplesResponse = await callErrorGroupSamplesApi({ + groupId: '0000000000000000000000000Error 1', + }); + + const errorId = errorsSamplesResponse.body.errorSampleIds[0]; + + const response = await callErrorSampleDetailsApi(errorId); + errorSampleDetailsResponse = response.body; + }); + + it('displays correct error grouping_key', () => { + expect(errorSampleDetailsResponse.error.error.grouping_key).to.equal( + '0000000000000000000000000Error 1' + ); + }); + + it('displays correct error message', () => { + expect(errorSampleDetailsResponse.error.error.exception?.[0].message).to.equal( + 'Error 1' + ); + }); + }); + }); + + describe('with sampled and unsampled transactions', () => { + let errorGroupSamplesResponse: ErrorGroupSamples; + + before(async () => { + const instance = service(serviceName, 'production', 'go').instance('a'); + const errorMessage = 'Error 1'; + const groupId = '0000000000000000000000000Error 1'; + + await apmSynthtraceEsClient.index([ + timerange(start, end) + .interval('15m') + .rate(1) + .generator((timestamp) => { + return [ + instance + .transaction('GET /api/foo') + .duration(100) + .timestamp(timestamp) + .sample(false) + .errors( + instance.error({ message: errorMessage }).timestamp(timestamp), + instance.error({ message: errorMessage }).timestamp(timestamp + 1) + ), + instance + .transaction('GET /api/foo') + .duration(100) + .timestamp(timestamp) + .sample(true) + .errors(instance.error({ message: errorMessage }).timestamp(timestamp)), + ]; + }), + ]); + + errorGroupSamplesResponse = (await callErrorGroupSamplesApi({ groupId })).body; + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('returns the errors in the correct order (sampled first, then unsampled)', () => { + const idsOfErrors = errorGroupSamplesResponse.errorSampleIds.map((id) => + parseInt(id, 10) + ); + + // this checks whether the order of indexing is different from the order that is returned + // if it is not, scoring/sorting is broken + expect(errorGroupSamplesResponse.errorSampleIds.length).to.be(3); + expect(idsOfErrors).to.not.eql(orderBy(idsOfErrors)); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/generate_mobile_data.ts similarity index 100% rename from x-pack/test/apm_api_integration/tests/mobile/generate_mobile_data.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/generate_mobile_data.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/index.ts new file mode 100644 index 000000000000..97d8e13256d6 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Mobile', () => { + loadTestFile(require.resolve('./crashes/crash_group_list.spec.ts')); + loadTestFile(require.resolve('./crashes/distribution.spec.ts')); + loadTestFile(require.resolve('./errors/group_id_samples.spec.ts')); + loadTestFile(require.resolve('./mobile_detailed_statistics_by_field.spec.ts')); + loadTestFile(require.resolve('./mobile_filters.spec.ts')); + loadTestFile(require.resolve('./mobile_http_requests_timeseries.spec.ts')); + loadTestFile(require.resolve('./mobile_location_stats.spec.ts')); + loadTestFile(require.resolve('./mobile_main_statistics_by_field.spec.ts')); + loadTestFile(require.resolve('./mobile_most_used_chart.spec.ts')); + loadTestFile(require.resolve('./mobile_sessions_timeseries.spec.ts')); + loadTestFile(require.resolve('./mobile_stats.spec.ts')); + loadTestFile(require.resolve('./mobile_terms_by_field.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_detailed_statistics_by_field.spec.ts similarity index 78% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_detailed_statistics_by_field.spec.ts index a8912989e295..134577dff920 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_detailed_statistics_by_field.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_detailed_statistics_by_field.spec.ts @@ -10,16 +10,18 @@ import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_value import { isEmpty } from 'lodash'; import moment from 'moment'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { generateMobileData, SERVICE_VERSIONS } from './generate_mobile_data'; type MobileDetailedStatisticsResponse = APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/detailed_statistics'>; -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -56,27 +58,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when( - 'Mobile detailed statistics when data is not loaded', - { config: 'basic', archives: [] }, - () => { - describe('when no data', () => { - it('handles empty state', async () => { - const response = await getMobileDetailedStatisticsByField({ - serviceName: 'foo', - field: 'service.version', - }); - expect(response).to.be.eql({ currentPeriod: {}, previousPeriod: {} }); + describe('Mobile detailed statistics ', () => { + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('when data is not loaded', () => { + it('handles empty state', async () => { + const response = await getMobileDetailedStatisticsByField({ + serviceName: 'foo', + field: 'service.version', }); + expect(response).to.be.eql({ currentPeriod: {}, previousPeriod: {} }); }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177388 - registry.when.skip( - 'Mobile detailed statistics when data is loaded', - { config: 'basic', archives: [] }, - () => { + }); + + describe('when data is loaded', () => { before(async () => { await generateMobileData({ apmSynthtraceEsClient, @@ -85,8 +84,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - after(() => apmSynthtraceEsClient.clean()); - describe('when comparison is disable', () => { it('returns current period data only', async () => { const response = await getMobileDetailedStatisticsByField({ @@ -133,6 +130,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); }); - } - ); + }); + }); } diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_filters.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_filters.spec.ts similarity index 88% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_filters.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_filters.spec.ts index edebde9f0d43..5c8d8499d329 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_filters.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_filters.spec.ts @@ -7,10 +7,10 @@ import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; type MobileFilters = APIReturnType<'GET /internal/apm/services/{serviceName}/mobile/filters'>; @@ -133,10 +133,11 @@ async function generateData({ ]); } -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -166,7 +167,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when('Mobile filters when data is not loaded', { config: 'basic', archives: [] }, () => { + describe('Mobile filters', () => { describe('when no data', () => { it('handles empty state', async () => { const response = await getMobileFilters({ serviceName: 'foo' }); @@ -175,30 +176,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177389 - registry.when.skip('Mobile filters', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, - }); - }); - - after(() => apmSynthtraceEsClient.clean()); describe('when data is loaded', () => { - let response: MobileFilters; - before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateData({ + apmSynthtraceEsClient, + start, + end, + }); response = await getMobileFilters({ serviceName: 'synth-android', environment: 'production', }); }); + after(() => apmSynthtraceEsClient.clean()); + let response: MobileFilters; + it('returns correct filters for device', () => { response.mobileFilters.map(({ key, options }) => { if (key === 'device') { diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_http_requests_timeseries.spec.ts similarity index 58% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_http_requests_timeseries.spec.ts index ccd4ddd23ca5..6a5a92a29b33 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_http_requests_timeseries.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_http_requests_timeseries.spec.ts @@ -7,13 +7,15 @@ import expect from '@kbn/expect'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { generateMobileData } from './generate_mobile_data'; -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T02:00:00.000Z').getTime(); @@ -47,27 +49,20 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); } - registry.when.skip( - 'Mobile HTTP requests without data loaded', - { config: 'basic', archives: [] }, - () => { - describe('when no data', () => { - it('handles empty state', async () => { - const response = await getHttpRequestsChart({ serviceName: 'foo' }); - expect(response.body.currentPeriod.timeseries).to.eql([]); - expect(response.body.previousPeriod.timeseries).to.eql([]); - expect(response.status).to.be(200); - }); + describe('Mobile HTTP requests ', () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getHttpRequestsChart({ serviceName: 'foo' }); + expect(response.body.currentPeriod.timeseries).to.eql([]); + expect(response.body.previousPeriod.timeseries).to.eql([]); + expect(response.status).to.be(200); }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177390 - registry.when.skip( - 'Mobile HTTP requests with data loaded', - { config: 'basic', archives: [] }, - () => { + }); + + describe('when data is loaded', () => { before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await generateMobileData({ apmSynthtraceEsClient, start, @@ -76,32 +71,29 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - it('returns timeseries for http requests chart', async () => { - const response = await getHttpRequestsChart({ - serviceName: 'synth-android', - offset: '1d', - }); - - expect(response.status).to.be(200); - expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( - true - ); - expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); + it('returns timeseries for http requests chart', async () => { + const response = await getHttpRequestsChart({ + serviceName: 'synth-android', + offset: '1d', }); - it('returns only current period timeseries when offset is not available', async () => { - const response = await getHttpRequestsChart({ serviceName: 'synth-android' }); + expect(response.status).to.be(200); + expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( + true + ); + expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); + }); - expect(response.status).to.be(200); - expect( - response.body.currentPeriod.timeseries.some((item) => item.y === 0 && item.x) - ).to.eql(true); + it('returns only current period timeseries when offset is not available', async () => { + const response = await getHttpRequestsChart({ serviceName: 'synth-android' }); - expect(response.body.currentPeriod.timeseries[0].y).to.eql(7); - expect(response.body.previousPeriod.timeseries).to.eql([]); - }); + expect(response.status).to.be(200); + expect( + response.body.currentPeriod.timeseries.some((item) => item.y === 0 && item.x) + ).to.eql(true); + + expect(response.body.currentPeriod.timeseries[0].y).to.eql(7); + expect(response.body.previousPeriod.timeseries).to.eql([]); }); describe('when filters are applied', () => { @@ -138,6 +130,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(ntcCell.body.currentPeriod.timeseries[0].y).to.eql(2); }); }); - } - ); + }); + }); } diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_location_stats.spec.ts similarity index 60% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_location_stats.spec.ts index ec82de406e0e..20098e9a671e 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_location_stats.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_location_stats.spec.ts @@ -7,10 +7,10 @@ import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; type MobileLocationStats = APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/location/stats'>; @@ -176,10 +176,11 @@ async function generateData({ ]); } -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -212,7 +213,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when('Location stats when data is not loaded', { config: 'basic', archives: [] }, () => { + describe('Location stats', () => { describe('when no data', () => { it('handles empty state', async () => { const response = await getMobileLocationStats({ serviceName: 'foo' }); @@ -230,111 +231,112 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); }); }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177396 - registry.when.skip('Location stats', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, - }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - let response: MobileLocationStats; + describe('Location stats with data', () => { before(async () => { - response = await getMobileLocationStats({ - serviceName: 'synth-android', - environment: 'production', + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateData({ + apmSynthtraceEsClient, + start, + end, }); }); - it('returns location for most sessions', () => { - const { location } = response.currentPeriod.mostSessions; - expect(location).to.be('China'); - }); + after(() => apmSynthtraceEsClient.clean()); - it('returns location for most requests', () => { - const { location } = response.currentPeriod.mostRequests; - expect(location).to.be('China'); - }); + describe('when data is loaded', () => { + let response: MobileLocationStats; - it('returns location for most crashes', () => { - const { location } = response.currentPeriod.mostCrashes; - expect(location).to.be('China'); - }); + before(async () => { + response = await getMobileLocationStats({ + serviceName: 'synth-android', + environment: 'production', + }); + }); - it('returns location for most launches', () => { - const { location } = response.currentPeriod.mostLaunches; - expect(location).to.be('China'); - }); - }); + it('returns location for most sessions', () => { + const { location } = response.currentPeriod.mostSessions; + expect(location).to.be('China'); + }); - describe('when filters are applied', () => { - it('returns empty state for filters with no results', async () => { - const response = await getMobileLocationStats({ - serviceName: 'synth-android', - environment: 'production', - kuery: `app.version:"none"`, + it('returns location for most requests', () => { + const { location } = response.currentPeriod.mostRequests; + expect(location).to.be('China'); }); - expect(response.currentPeriod.mostSessions.value).to.eql(0); - expect(response.currentPeriod.mostRequests.value).to.eql(0); - expect(response.currentPeriod.mostCrashes.value).to.eql(0); - expect(response.currentPeriod.mostLaunches.value).to.eql(0); + it('returns location for most crashes', () => { + const { location } = response.currentPeriod.mostCrashes; + expect(location).to.be('China'); + }); - expect(response.currentPeriod.mostSessions.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect(response.currentPeriod.mostRequests.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect(response.currentPeriod.mostCrashes.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect(response.currentPeriod.mostLaunches.timeseries.every((item) => item.y === 0)).to.eql( - true - ); + it('returns location for most launches', () => { + const { location } = response.currentPeriod.mostLaunches; + expect(location).to.be('China'); + }); }); - it('returns the correct values when single filter is applied', async () => { - const response = await getMobileLocationStats({ - serviceName: 'synth-android', - environment: 'production', - kuery: `service.version:"1.1"`, + describe('when filters are applied', () => { + it('returns empty state for filters with no results', async () => { + const response = await getMobileLocationStats({ + serviceName: 'synth-android', + environment: 'production', + kuery: `app.version:"none"`, + }); + + expect(response.currentPeriod.mostSessions.value).to.eql(0); + expect(response.currentPeriod.mostRequests.value).to.eql(0); + expect(response.currentPeriod.mostCrashes.value).to.eql(0); + expect(response.currentPeriod.mostLaunches.value).to.eql(0); + + expect( + response.currentPeriod.mostSessions.timeseries.every((item) => item.y === 0) + ).to.eql(true); + expect( + response.currentPeriod.mostRequests.timeseries.every((item) => item.y === 0) + ).to.eql(true); + expect( + response.currentPeriod.mostCrashes.timeseries.every((item) => item.y === 0) + ).to.eql(true); + expect( + response.currentPeriod.mostLaunches.timeseries.every((item) => item.y === 0) + ).to.eql(true); }); - expect(response.currentPeriod.mostSessions.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostCrashes.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostRequests.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostLaunches.timeseries[0].y).to.eql(1); + it('returns the correct values when single filter is applied', async () => { + const response = await getMobileLocationStats({ + serviceName: 'synth-android', + environment: 'production', + kuery: `service.version:"1.1"`, + }); - expect(response.currentPeriod.mostSessions.value).to.eql(3); - expect(response.currentPeriod.mostRequests.value).to.eql(3); - expect(response.currentPeriod.mostCrashes.value).to.eql(3); - expect(response.currentPeriod.mostLaunches.value).to.eql(3); - }); + expect(response.currentPeriod.mostSessions.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostCrashes.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostRequests.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostLaunches.timeseries[0].y).to.eql(1); - it('returns the correct values when multiple filters are applied', async () => { - const response = await getMobileLocationStats({ - serviceName: 'synth-android', - kuery: `service.version:"1.1" and service.environment: "production"`, + expect(response.currentPeriod.mostSessions.value).to.eql(3); + expect(response.currentPeriod.mostRequests.value).to.eql(3); + expect(response.currentPeriod.mostCrashes.value).to.eql(3); + expect(response.currentPeriod.mostLaunches.value).to.eql(3); }); - expect(response.currentPeriod.mostSessions.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostCrashes.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostRequests.timeseries[0].y).to.eql(1); - expect(response.currentPeriod.mostLaunches.timeseries[0].y).to.eql(1); + it('returns the correct values when multiple filters are applied', async () => { + const response = await getMobileLocationStats({ + serviceName: 'synth-android', + kuery: `service.version:"1.1" and service.environment: "production"`, + }); - expect(response.currentPeriod.mostSessions.value).to.eql(3); - expect(response.currentPeriod.mostRequests.value).to.eql(3); - expect(response.currentPeriod.mostCrashes.value).to.eql(3); - expect(response.currentPeriod.mostLaunches.value).to.eql(3); + expect(response.currentPeriod.mostSessions.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostCrashes.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostRequests.timeseries[0].y).to.eql(1); + expect(response.currentPeriod.mostLaunches.timeseries[0].y).to.eql(1); + + expect(response.currentPeriod.mostSessions.value).to.eql(3); + expect(response.currentPeriod.mostRequests.value).to.eql(3); + expect(response.currentPeriod.mostCrashes.value).to.eql(3); + expect(response.currentPeriod.mostLaunches.value).to.eql(3); + }); }); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_main_statistics_by_field.spec.ts similarity index 59% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_main_statistics_by_field.spec.ts index 945ed5970e00..0e49624be2a9 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_main_statistics_by_field.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_main_statistics_by_field.spec.ts @@ -4,12 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; const GALAXY_DURATION = 500; const HUAWEI_DURATION = 20; @@ -126,10 +125,11 @@ async function generateData({ ]); } -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -162,91 +162,88 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when( - 'Mobile main statistics when data is not loaded', - { config: 'basic', archives: [] }, - () => { - describe('when no data', () => { - it('handles empty state', async () => { - const response = await getMobileMainStatisticsByField({ - serviceName: 'foo', - field: 'service.version', - }); - expect(response.mainStatistics.length).to.be(0); + describe('Mobile main statistics', () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'foo', + field: 'service.version', }); - }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177395 - registry.when.skip('Mobile main statistics', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, + expect(response.mainStatistics.length).to.be(0); }); }); - after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - const huaweiLatency = calculateLatency(HUAWEI_DURATION); - const galaxyLatency = calculateLatency(GALAXY_DURATION); - const huaweiThroughput = calculateThroughput({ start, end }); - const galaxyThroughput = calculateThroughput({ start, end }); + describe('Mobile main statistics', () => { + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); - it('returns the correct data for App version', async () => { - const response = await getMobileMainStatisticsByField({ - serviceName: 'synth-android', - environment: 'production', - field: 'service.version', + await generateData({ + apmSynthtraceEsClient, + start, + end, }); - const fieldValues = response.mainStatistics.map((item) => item.name); + }); - expect(fieldValues).to.be.eql(SERVICE_VERSIONS); + after(() => apmSynthtraceEsClient.clean()); - const latencyValues = response.mainStatistics.map((item) => item.latency); + describe('when data is loaded', () => { + const huaweiLatency = calculateLatency(HUAWEI_DURATION); + const galaxyLatency = calculateLatency(GALAXY_DURATION); + const huaweiThroughput = calculateThroughput({ start, end }); + const galaxyThroughput = calculateThroughput({ start, end }); - expect(latencyValues).to.be.eql([galaxyLatency, huaweiLatency]); + it('returns the correct data for App version', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'service.version', + }); + const fieldValues = response.mainStatistics.map((item) => item.name); - const throughputValues = response.mainStatistics.map((item) => item.throughput); - expect(throughputValues).to.be.eql([galaxyThroughput, huaweiThroughput]); - }); - it('returns the correct data for Os version', async () => { - const response = await getMobileMainStatisticsByField({ - serviceName: 'synth-android', - environment: 'production', - field: 'host.os.version', + expect(fieldValues).to.be.eql(SERVICE_VERSIONS); + + const latencyValues = response.mainStatistics.map((item) => item.latency); + + expect(latencyValues).to.be.eql([galaxyLatency, huaweiLatency]); + + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([galaxyThroughput, huaweiThroughput]); }); + it('returns the correct data for Os version', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'host.os.version', + }); - const fieldValues = response.mainStatistics.map((item) => item.name); + const fieldValues = response.mainStatistics.map((item) => item.name); - expect(fieldValues).to.be.eql(OS_VERSIONS); + expect(fieldValues).to.be.eql(OS_VERSIONS); - const latencyValues = response.mainStatistics.map((item) => item.latency); + const latencyValues = response.mainStatistics.map((item) => item.latency); - expect(latencyValues).to.be.eql([galaxyLatency, huaweiLatency]); + expect(latencyValues).to.be.eql([galaxyLatency, huaweiLatency]); - const throughputValues = response.mainStatistics.map((item) => item.throughput); - expect(throughputValues).to.be.eql([galaxyThroughput, huaweiThroughput]); - }); - it('returns the correct data for Devices', async () => { - const response = await getMobileMainStatisticsByField({ - serviceName: 'synth-android', - environment: 'production', - field: 'device.model.identifier', + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([galaxyThroughput, huaweiThroughput]); }); - const fieldValues = response.mainStatistics.map((item) => item.name); + it('returns the correct data for Devices', async () => { + const response = await getMobileMainStatisticsByField({ + serviceName: 'synth-android', + environment: 'production', + field: 'device.model.identifier', + }); + const fieldValues = response.mainStatistics.map((item) => item.name); - expect(fieldValues).to.be.eql(['HUAWEI P2-0000', 'SM-G973F']); + expect(fieldValues).to.be.eql(['HUAWEI P2-0000', 'SM-G973F']); - const latencyValues = response.mainStatistics.map((item) => item.latency); + const latencyValues = response.mainStatistics.map((item) => item.latency); - expect(latencyValues).to.be.eql([huaweiLatency, galaxyLatency]); + expect(latencyValues).to.be.eql([huaweiLatency, galaxyLatency]); - const throughputValues = response.mainStatistics.map((item) => item.throughput); - expect(throughputValues).to.be.eql([huaweiThroughput, galaxyThroughput]); + const throughputValues = response.mainStatistics.map((item) => item.throughput); + expect(throughputValues).to.be.eql([huaweiThroughput, galaxyThroughput]); + }); }); }); }); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_most_used_chart.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_most_used_chart.spec.ts new file mode 100644 index 000000000000..205b5125d944 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_most_used_chart.spec.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import expect from '@kbn/expect'; +import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { generateMobileData } from './generate_mobile_data'; + +type MostUsedCharts = + APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/most_used_charts'>; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; + + async function getMobileMostUsedCharts({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + transactionType = 'mobile', + }: { + environment?: string; + kuery?: string; + serviceName: string; + transactionType?: string; + }) { + return await apmApiClient + .readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/most_used_charts', + params: { + path: { serviceName }, + query: { + environment, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + kuery, + transactionType, + }, + }, + }) + .then(({ body }) => body); + } + + describe('Most used charts', () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response: MostUsedCharts = await getMobileMostUsedCharts({ serviceName: 'foo' }); + expect(response.mostUsedCharts.length).to.eql(4); + expect(response.mostUsedCharts.every((chart) => chart.options.length === 0)).to.eql(true); + }); + }); + + describe('Mobile stats', () => { + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateMobileData({ + apmSynthtraceEsClient, + start, + end, + }); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('when data is loaded', () => { + let response: MostUsedCharts; + + before(async () => { + response = await getMobileMostUsedCharts({ + serviceName: 'synth-android', + environment: 'production', + }); + }); + + it('should get the top 5 and the other option only', () => { + const deviceOptions = response.mostUsedCharts.find( + (chart) => chart.key === 'device' + )?.options; + expect(deviceOptions?.length).to.eql(6); + expect(deviceOptions?.find((option) => option.key === 'other')).to.not.be(undefined); + }); + + it('should get network connection type object from span events', () => { + const nctOptions = response.mostUsedCharts.find( + (chart) => chart.key === 'netConnectionType' + )?.options; + expect(nctOptions?.length).to.eql(2); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_sessions_timeseries.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_sessions_timeseries.spec.ts new file mode 100644 index 000000000000..c43284767b7b --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_sessions_timeseries.spec.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import expect from '@kbn/expect'; +import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { generateMobileData } from './generate_mobile_data'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + const start = new Date('2023-01-01T00:00:00.000Z').getTime(); + const end = new Date('2023-01-01T02:00:00.000Z').getTime(); + + async function getSessionsChart({ + environment = ENVIRONMENT_ALL.value, + kuery = '', + serviceName, + transactionType = 'mobile', + offset, + }: { + environment?: string; + kuery?: string; + serviceName: string; + transactionType?: string; + offset?: string; + }) { + return await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions', + params: { + path: { serviceName }, + query: { + environment, + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + offset, + kuery, + transactionType, + }, + }, + }); + } + + describe('Sessions charts', () => { + describe('when no data', () => { + it('handles empty state', async () => { + const response = await getSessionsChart({ serviceName: 'foo' }); + expect(response.body.currentPeriod.timeseries).to.eql([]); + expect(response.body.previousPeriod.timeseries).to.eql([]); + expect(response.status).to.be(200); + }); + }); + + describe('with data loaded', () => { + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateMobileData({ + apmSynthtraceEsClient, + start, + end, + }); + }); + + after(() => apmSynthtraceEsClient.clean()); + + describe('when data is loaded', () => { + it('returns timeseries for sessions chart', async () => { + const response = await getSessionsChart({ serviceName: 'synth-android', offset: '1d' }); + + expect(response.status).to.be(200); + expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( + true + ); + + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); + expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); + }); + + it('returns only current period timeseries when offset is not available', async () => { + const response = await getSessionsChart({ serviceName: 'synth-android' }); + + expect(response.status).to.be(200); + expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( + true + ); + + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); + expect(response.body.previousPeriod.timeseries).to.eql([]); + }); + }); + + describe('when filters are applied', () => { + it('returns empty state for filters', async () => { + const response = await getSessionsChart({ + serviceName: 'synth-android', + environment: 'production', + kuery: `app.version:"none"`, + }); + + expect(response.body.currentPeriod.timeseries.every((item) => item.y === 0)).to.eql(true); + expect(response.body.previousPeriod.timeseries.every((item) => item.y === 0)).to.eql( + true + ); + }); + + it('returns the correct values filter is applied', async () => { + const response = await getSessionsChart({ + serviceName: 'synth-android', + environment: 'production', + kuery: `transaction.name : "Start View - View Appearing"`, + }); + + expect(response.status).to.be(200); + expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( + true + ); + + expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); + expect(response.body.previousPeriod.timeseries).to.eql([]); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_stats.spec.ts similarity index 56% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_stats.spec.ts index 0b1e71471a2b..22281cd39295 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_stats.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_stats.spec.ts @@ -7,11 +7,11 @@ import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; import { meanBy, sumBy } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; type MobileStats = APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/stats'>; @@ -134,10 +134,11 @@ async function generateData({ ]); } -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -170,7 +171,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when('Mobile stats when data is not loaded', { config: 'basic', archives: [] }, () => { + describe('Mobile stats', () => { describe('when no data', () => { it('handles empty state', async () => { const response = await getMobileStats({ serviceName: 'foo' }); @@ -182,109 +183,110 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); }); }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177392 - registry.when.skip('Mobile stats', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, - }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - let response: MobileStats; + describe('Mobile stats', () => { before(async () => { - response = await getMobileStats({ - serviceName: 'synth-android', - environment: 'production', + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateData({ + apmSynthtraceEsClient, + start, + end, }); }); - it('returns same sessions', () => { - const { value, timeseries } = response.currentPeriod.sessions; - const timeseriesTotal = sumBy(timeseries, 'y'); - expect(value).to.be(timeseriesTotal); - }); + after(() => apmSynthtraceEsClient.clean()); - it('returns same requests', () => { - const { value, timeseries } = response.currentPeriod.requests; - const timeseriesTotal = sumBy(timeseries, 'y'); - expect(value).to.be(timeseriesTotal); - }); + describe('when data is loaded', () => { + let response: MobileStats; - it('returns same crashes', () => { - const { value, timeseries } = response.currentPeriod.crashRate; - const timeseriesMean = meanBy( - timeseries.filter((bucket) => bucket.y !== 0), - 'y' - ); - expect(value).to.be(timeseriesMean); - }); - it('returns same launch times', () => { - const { value, timeseries } = response.currentPeriod.launchTimes; - const timeseriesMean = meanBy( - timeseries.filter((bucket) => bucket.y !== null), - 'y' - ); - expect(value).to.be(timeseriesMean); - }); - }); + before(async () => { + response = await getMobileStats({ + serviceName: 'synth-android', + environment: 'production', + }); + }); - describe('when filters are applied', () => { - it('returns empty state for filters', async () => { - const response = await getMobileStats({ - serviceName: 'synth-android', - environment: 'production', - kuery: `app.version:"none"`, + it('returns same sessions', () => { + const { value, timeseries } = response.currentPeriod.sessions; + const timeseriesTotal = sumBy(timeseries, 'y'); + expect(value).to.be(timeseriesTotal); }); - expect(response.currentPeriod.sessions.value).to.eql(0); - expect(response.currentPeriod.requests.value).to.eql(0); - expect(response.currentPeriod.crashRate.value).to.eql(0); - expect(response.currentPeriod.launchTimes.value).to.eql(null); + it('returns same requests', () => { + const { value, timeseries } = response.currentPeriod.requests; + const timeseriesTotal = sumBy(timeseries, 'y'); + expect(value).to.be(timeseriesTotal); + }); - expect(response.currentPeriod.sessions.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect(response.currentPeriod.requests.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect(response.currentPeriod.crashRate.timeseries.every((item) => item.y === 0)).to.eql( - true - ); - expect( - response.currentPeriod.launchTimes.timeseries.every((item) => item.y === null) - ).to.eql(true); + it('returns same crashes', () => { + const { value, timeseries } = response.currentPeriod.crashRate; + const timeseriesMean = meanBy( + timeseries.filter((bucket) => bucket.y !== 0), + 'y' + ); + expect(value).to.be(timeseriesMean); + }); + it('returns same launch times', () => { + const { value, timeseries } = response.currentPeriod.launchTimes; + const timeseriesMean = meanBy( + timeseries.filter((bucket) => bucket.y !== null), + 'y' + ); + expect(value).to.be(timeseriesMean); + }); }); - it('returns the correct values when single filter is applied', async () => { - const response = await getMobileStats({ - serviceName: 'synth-android', - environment: 'production', - kuery: `service.version:"2.3"`, + describe('when filters are applied', () => { + it('returns empty state for filters', async () => { + const response = await getMobileStats({ + serviceName: 'synth-android', + environment: 'production', + kuery: `app.version:"none"`, + }); + + expect(response.currentPeriod.sessions.value).to.eql(0); + expect(response.currentPeriod.requests.value).to.eql(0); + expect(response.currentPeriod.crashRate.value).to.eql(0); + expect(response.currentPeriod.launchTimes.value).to.eql(null); + + expect(response.currentPeriod.sessions.timeseries.every((item) => item.y === 0)).to.eql( + true + ); + expect(response.currentPeriod.requests.timeseries.every((item) => item.y === 0)).to.eql( + true + ); + expect(response.currentPeriod.crashRate.timeseries.every((item) => item.y === 0)).to.eql( + true + ); + expect( + response.currentPeriod.launchTimes.timeseries.every((item) => item.y === null) + ).to.eql(true); }); - expect(response.currentPeriod.sessions.value).to.eql(3); - expect(response.currentPeriod.requests.value).to.eql(0); - expect(response.currentPeriod.crashRate.value).to.eql(3); - expect(response.currentPeriod.launchTimes.value).to.eql(null); - }); + it('returns the correct values when single filter is applied', async () => { + const response = await getMobileStats({ + serviceName: 'synth-android', + environment: 'production', + kuery: `service.version:"2.3"`, + }); + + expect(response.currentPeriod.sessions.value).to.eql(3); + expect(response.currentPeriod.requests.value).to.eql(0); + expect(response.currentPeriod.crashRate.value).to.eql(3); + expect(response.currentPeriod.launchTimes.value).to.eql(null); + }); - it('returns the correct values when multiple filters are applied', async () => { - const response = await getMobileStats({ - serviceName: 'synth-android', - kuery: `service.version:"1.2" and service.environment: "production"`, + it('returns the correct values when multiple filters are applied', async () => { + const response = await getMobileStats({ + serviceName: 'synth-android', + kuery: `service.version:"1.2" and service.environment: "production"`, + }); + expect(response.currentPeriod.sessions.value).to.eql(3); + expect(response.currentPeriod.requests.value).to.eql(3); + expect(response.currentPeriod.crashRate.value).to.eql(1); + expect(response.currentPeriod.launchTimes.value).to.eql(100); }); - expect(response.currentPeriod.sessions.value).to.eql(3); - expect(response.currentPeriod.requests.value).to.eql(3); - expect(response.currentPeriod.crashRate.value).to.eql(1); - expect(response.currentPeriod.launchTimes.value).to.eql(100); }); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_terms_by_field.spec.ts similarity index 70% rename from x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_terms_by_field.spec.ts index 3ccdba0a2423..8dd414210937 100644 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_terms_by_field.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/mobile/mobile_terms_by_field.spec.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; // we generate 3 transactions per each mobile device // timerange 15min, interval 5m, rate 1 @@ -124,10 +124,11 @@ async function generateData({ ]); } -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + + const synthtrace = getService('synthtrace'); + let apmSynthtraceEsClient: ApmSynthtraceEsClient; const start = new Date('2023-01-01T00:00:00.000Z').getTime(); const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; @@ -163,7 +164,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .then(({ body }) => body); } - registry.when('Mobile terms when data is not loaded', { config: 'basic', archives: [] }, () => { + describe('Mobile terms', () => { describe('when no data', () => { it('handles empty state', async () => { const response = await getMobileTermsByField({ @@ -183,66 +184,67 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.terms).to.eql([]); }); }); - }); - // FLAKY: https://github.com/elastic/kibana/issues/177498 - registry.when.skip('Mobile terms', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, + describe('Mobile terms', () => { + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await generateData({ + apmSynthtraceEsClient, + start, + end, + }); }); - }); - after(() => apmSynthtraceEsClient.clean()); + after(() => apmSynthtraceEsClient.clean()); - describe('when data is loaded', () => { - it('returns mobile devices', async () => { - const response = await getMobileTermsByField({ - serviceName: 'synth-android', - environment: 'production', - fieldName: 'device.model.identifier', - size: 10, + describe('when data is loaded', () => { + it('returns mobile devices', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'device.model.identifier', + size: 10, + }); + expect(response.terms).to.eql([ + { label: 'HUAWEI P2-0000', count: 3 }, + { label: 'SM-G973F', count: 3 }, + ]); }); - expect(response.terms).to.eql([ - { label: 'HUAWEI P2-0000', count: 3 }, - { label: 'SM-G973F', count: 3 }, - ]); - }); - it('returns mobile versions', async () => { - const response = await getMobileTermsByField({ - serviceName: 'synth-android', - environment: 'production', - fieldName: 'service.version', - size: 10, + it('returns mobile versions', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'service.version', + size: 10, + }); + expect(response.terms).to.eql([ + { + label: '1.2', + count: 3, + }, + { + label: '2.3', + count: 3, + }, + ]); }); - expect(response.terms).to.eql([ - { - label: '1.2', - count: 3, - }, - { - label: '2.3', - count: 3, - }, - ]); - }); - it('return the most used mobile version', async () => { - const response = await getMobileTermsByField({ - serviceName: 'synth-android', - environment: 'production', - fieldName: 'service.version', - size: 1, + it('return the most used mobile version', async () => { + const response = await getMobileTermsByField({ + serviceName: 'synth-android', + environment: 'production', + fieldName: 'service.version', + size: 1, + }); + expect(response.terms).to.eql([ + { + label: '1.2', + count: 3, + }, + ]); }); - expect(response.terms).to.eql([ - { - label: '1.2', - count: 3, - }, - ]); }); }); }); diff --git a/x-pack/test/apm_api_integration/tests/mobile/crashes/crash_group_list.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/crashes/crash_group_list.spec.ts deleted file mode 100644 index a36036b3ec8e..000000000000 --- a/x-pack/test/apm_api_integration/tests/mobile/crashes/crash_group_list.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { - APIClientRequestParamsOf, - APIReturnType, -} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -type ErrorGroups = - APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics'>['errorGroups']; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const serviceName = 'synth-swift'; - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - async function callApi( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics'>['params'] - > - ) { - return await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics', - params: { - path: { serviceName, ...overrides?.path }, - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - environment: 'ENVIRONMENT_ALL', - kuery: '', - ...overrides?.query, - }, - }, - }); - } - - registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { - it('handles empty state', async () => { - const response = await callApi(); - expect(response.status).to.be(200); - expect(response.body.errorGroups).to.empty(); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177651 - registry.when.skip('when data is loaded', { config: 'basic', archives: [] }, () => { - describe('errors group', () => { - const appleTransaction = { - name: 'GET /apple 🍎 ', - successRate: 75, - failureRate: 25, - }; - - const bananaTransaction = { - name: 'GET /banana 🍌', - successRate: 50, - failureRate: 50, - }; - - before(async () => { - const serviceInstance = apm - .service({ name: serviceName, environment: 'production', agentName: 'swift' }) - .instance('instance-a'); - - await apmSynthtraceEsClient.index([ - timerange(start, end) - .interval('1m') - .rate(appleTransaction.successRate) - .generator((timestamp) => - serviceInstance - .transaction({ transactionName: appleTransaction.name }) - .timestamp(timestamp) - .duration(1000) - .success() - ), - timerange(start, end) - .interval('1m') - .rate(appleTransaction.failureRate) - .generator((timestamp) => - serviceInstance - .transaction({ transactionName: appleTransaction.name }) - .errors( - serviceInstance - .crash({ - message: 'crash 1', - }) - .timestamp(timestamp) - ) - .duration(1000) - .timestamp(timestamp) - .failure() - ), - timerange(start, end) - .interval('1m') - .rate(bananaTransaction.successRate) - .generator((timestamp) => - serviceInstance - .transaction({ transactionName: bananaTransaction.name }) - .timestamp(timestamp) - .duration(1000) - .success() - ), - timerange(start, end) - .interval('1m') - .rate(bananaTransaction.failureRate) - .generator((timestamp) => - serviceInstance - .transaction({ transactionName: bananaTransaction.name }) - .errors( - serviceInstance - .crash({ - message: 'crash 2', - }) - .timestamp(timestamp) - ) - .duration(1000) - .timestamp(timestamp) - .failure() - ), - ]); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('returns the correct data', () => { - let errorGroups: ErrorGroups; - before(async () => { - const response = await callApi(); - errorGroups = response.body.errorGroups; - }); - it('returns correct number of crashes', () => { - expect(errorGroups.length).to.equal(2); - expect(errorGroups.map((error) => error.name).sort()).to.eql(['crash 1', 'crash 2']); - }); - - it('returns correct occurrences', () => { - const numberOfBuckets = 15; - expect(errorGroups.map((error) => error.occurrences).sort()).to.eql([ - appleTransaction.failureRate * numberOfBuckets, - bananaTransaction.failureRate * numberOfBuckets, - ]); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/mobile/crashes/distribution.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/crashes/distribution.spec.ts deleted file mode 100644 index 2fabce70d269..000000000000 --- a/x-pack/test/apm_api_integration/tests/mobile/crashes/distribution.spec.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { first, last, sumBy } from 'lodash'; -import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; -import { - APIClientRequestParamsOf, - APIReturnType, -} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { config, generateData } from './generate_data'; - -type ErrorsDistribution = - APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution'>; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const serviceName = 'synth-swift'; - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - async function callApi( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution'>['params'] - > - ) { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/mobile-services/{serviceName}/crashes/distribution', - params: { - path: { - serviceName, - ...overrides?.path, - }, - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - environment: 'ENVIRONMENT_ALL', - kuery: '', - ...overrides?.query, - }, - }, - }); - return response; - } - - registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await callApi(); - expect(response.status).to.be(200); - expect(response.body.currentPeriod.length).to.be(0); - expect(response.body.previousPeriod.length).to.be(0); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177652 - registry.when.skip('when data is loaded', { config: 'basic', archives: [] }, () => { - describe('errors distribution', () => { - const { appleTransaction, bananaTransaction } = config; - before(async () => { - await generateData({ serviceName, start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('without comparison', () => { - let errorsDistribution: ErrorsDistribution; - before(async () => { - const response = await callApi(); - errorsDistribution = response.body; - }); - - it('displays combined number of occurrences', () => { - const countSum = sumBy(errorsDistribution.currentPeriod, 'y'); - const numberOfBuckets = 15; - expect(countSum).to.equal( - (appleTransaction.failureRate + bananaTransaction.failureRate) * numberOfBuckets - ); - }); - - describe('displays correct start in errors distribution chart', () => { - let errorsDistributionWithComparison: ErrorsDistribution; - before(async () => { - const responseWithComparison = await callApi({ - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - offset: '15m', - }, - }); - errorsDistributionWithComparison = responseWithComparison.body; - }); - it('has same start time when comparison is enabled', () => { - expect(first(errorsDistribution.currentPeriod)?.x).to.equal( - first(errorsDistributionWithComparison.currentPeriod)?.x - ); - }); - }); - }); - - describe('displays occurrences for type "apple transaction" only', () => { - let errorsDistribution: ErrorsDistribution; - before(async () => { - const response = await callApi({ - query: { kuery: `error.exception.type:"${appleTransaction.name}"` }, - }); - errorsDistribution = response.body; - }); - it('displays combined number of occurrences', () => { - const countSum = sumBy(errorsDistribution.currentPeriod, 'y'); - const numberOfBuckets = 15; - expect(countSum).to.equal(appleTransaction.failureRate * numberOfBuckets); - }); - }); - - describe('with comparison', () => { - describe('when data is returned', () => { - let errorsDistribution: ErrorsDistribution; - before(async () => { - const fiveMinutes = 5 * 60 * 1000; - const response = await callApi({ - query: { - start: new Date(end - fiveMinutes).toISOString(), - end: new Date(end).toISOString(), - offset: '5m', - }, - }); - errorsDistribution = response.body; - }); - it('returns some data', () => { - const hasCurrentPeriodData = errorsDistribution.currentPeriod.some(({ y }) => - isFiniteNumber(y) - ); - - const hasPreviousPeriodData = errorsDistribution.previousPeriod.some(({ y }) => - isFiniteNumber(y) - ); - - expect(hasCurrentPeriodData).to.equal(true); - expect(hasPreviousPeriodData).to.equal(true); - }); - - it('has same start time for both periods', () => { - expect(first(errorsDistribution.currentPeriod)?.x).to.equal( - first(errorsDistribution.previousPeriod)?.x - ); - }); - - it('has same end time for both periods', () => { - expect(last(errorsDistribution.currentPeriod)?.x).to.equal( - last(errorsDistribution.previousPeriod)?.x - ); - }); - - it('returns same number of buckets for both periods', () => { - expect(errorsDistribution.currentPeriod.length).to.equal( - errorsDistribution.previousPeriod.length - ); - }); - }); - - describe('when no data is returned', () => { - let errorsDistribution: ErrorsDistribution; - before(async () => { - const response = await callApi({ - query: { - start: '2021-01-03T00:00:00.000Z', - end: '2021-01-03T00:15:00.000Z', - offset: '1d', - }, - }); - errorsDistribution = response.body; - }); - - it('has same start time for both periods', () => { - expect(first(errorsDistribution.currentPeriod)?.x).to.equal( - first(errorsDistribution.previousPeriod)?.x - ); - }); - - it('has same end time for both periods', () => { - expect(last(errorsDistribution.currentPeriod)?.x).to.equal( - last(errorsDistribution.previousPeriod)?.x - ); - }); - - it('returns same number of buckets for both periods', () => { - expect(errorsDistribution.currentPeriod.length).to.equal( - errorsDistribution.previousPeriod.length - ); - }); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/mobile/errors/group_id_samples.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/errors/group_id_samples.spec.ts deleted file mode 100644 index 129cbe2a7180..000000000000 --- a/x-pack/test/apm_api_integration/tests/mobile/errors/group_id_samples.spec.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { timerange } from '@kbn/apm-synthtrace-client'; -import { service } from '@kbn/apm-synthtrace-client/src/lib/apm/service'; -import { orderBy } from 'lodash'; -import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import { config, generateData } from './generate_data'; - -type ErrorGroupSamples = - APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>; - -type ErrorSampleDetails = - APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}'>; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const serviceName = 'synth-go'; - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - async function callErrorGroupSamplesApi({ groupId }: { groupId: string }) { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', - params: { - path: { - serviceName, - groupId, - }, - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - environment: 'ENVIRONMENT_ALL', - kuery: '', - }, - }, - }); - return response; - } - - async function callErrorSampleDetailsApi(errorId: string) { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}', - params: { - path: { - serviceName, - groupId: 'foo', - errorId, - }, - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - environment: 'ENVIRONMENT_ALL', - kuery: '', - }, - }, - }); - return response; - } - - registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await callErrorGroupSamplesApi({ groupId: 'foo' }); - expect(response.status).to.be(200); - expect(response.body.occurrencesCount).to.be(0); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177654 - registry.when.skip('when samples data is loaded', { config: 'basic', archives: [] }, () => { - const { bananaTransaction } = config; - describe('error group id', () => { - before(async () => { - await generateData({ serviceName, start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('return correct data', () => { - let errorsSamplesResponse: ErrorGroupSamples; - before(async () => { - const response = await callErrorGroupSamplesApi({ - groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', - }); - errorsSamplesResponse = response.body; - }); - - it('displays correct number of occurrences', () => { - const numberOfBuckets = 15; - expect(errorsSamplesResponse.occurrencesCount).to.equal( - bananaTransaction.failureRate * numberOfBuckets - ); - }); - }); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177665 - registry.when.skip('when error sample data is loaded', { config: 'basic', archives: [] }, () => { - describe('error sample id', () => { - before(async () => { - await generateData({ serviceName, start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('return correct data', () => { - let errorSampleDetailsResponse: ErrorSampleDetails; - before(async () => { - const errorsSamplesResponse = await callErrorGroupSamplesApi({ - groupId: '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03', - }); - - const errorId = errorsSamplesResponse.body.errorSampleIds[0]; - - const response = await callErrorSampleDetailsApi(errorId); - errorSampleDetailsResponse = response.body; - }); - - it('displays correct error grouping_key', () => { - expect(errorSampleDetailsResponse.error.error.grouping_key).to.equal( - '98b75903135eac35ad42419bd3b45cf8b4270c61cbd0ede0f7e8c8a9ac9fdb03' - ); - }); - - it('displays correct error message', () => { - expect(errorSampleDetailsResponse.error.error.exception?.[0].message).to.equal('Error 1'); - }); - }); - }); - - describe('with sampled and unsampled transactions', () => { - let errorGroupSamplesResponse: ErrorGroupSamples; - - before(async () => { - const instance = service(serviceName, 'production', 'go').instance('a'); - const errorMessage = 'Error 1'; - const groupId = getErrorGroupingKey(errorMessage); - - await apmSynthtraceEsClient.index([ - timerange(start, end) - .interval('15m') - .rate(1) - .generator((timestamp) => { - return [ - instance - .transaction('GET /api/foo') - .duration(100) - .timestamp(timestamp) - .sample(false) - .errors( - instance.error({ message: errorMessage }).timestamp(timestamp), - instance.error({ message: errorMessage }).timestamp(timestamp + 1) - ), - instance - .transaction('GET /api/foo') - .duration(100) - .timestamp(timestamp) - .sample(true) - .errors(instance.error({ message: errorMessage }).timestamp(timestamp)), - ]; - }), - ]); - - errorGroupSamplesResponse = (await callErrorGroupSamplesApi({ groupId })).body; - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('returns the errors in the correct order (sampled first, then unsampled)', () => { - const idsOfErrors = errorGroupSamplesResponse.errorSampleIds.map((id) => parseInt(id, 10)); - - // this checks whether the order of indexing is different from the order that is returned - // if it is not, scoring/sorting is broken - expect(errorGroupSamplesResponse.errorSampleIds.length).to.be(3); - expect(idsOfErrors).to.not.eql(orderBy(idsOfErrors)); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts deleted file mode 100644 index cde19d07344d..000000000000 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_most_used_chart.spec.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { APIReturnType } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { generateMobileData } from './generate_mobile_data'; - -type MostUsedCharts = - APIReturnType<'GET /internal/apm/mobile-services/{serviceName}/most_used_charts'>; - -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const start = new Date('2023-01-01T00:00:00.000Z').getTime(); - const end = new Date('2023-01-01T00:15:00.000Z').getTime() - 1; - - async function getMobileMostUsedCharts({ - environment = ENVIRONMENT_ALL.value, - kuery = '', - serviceName, - transactionType = 'mobile', - }: { - environment?: string; - kuery?: string; - serviceName: string; - transactionType?: string; - }) { - return await apmApiClient - .readUser({ - endpoint: 'GET /internal/apm/mobile-services/{serviceName}/most_used_charts', - params: { - path: { serviceName }, - query: { - environment, - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - kuery, - transactionType, - }, - }, - }) - .then(({ body }) => body); - } - - registry.when( - 'Most used charts when data is not loaded', - { config: 'basic', archives: [] }, - () => { - describe('when no data', () => { - it('handles empty state', async () => { - const response: MostUsedCharts = await getMobileMostUsedCharts({ serviceName: 'foo' }); - expect(response.mostUsedCharts.length).to.eql(4); - expect(response.mostUsedCharts.every((chart) => chart.options.length === 0)).to.eql(true); - }); - }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177394 - registry.when.skip('Mobile stats', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateMobileData({ - apmSynthtraceEsClient, - start, - end, - }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - let response: MostUsedCharts; - - before(async () => { - response = await getMobileMostUsedCharts({ - serviceName: 'synth-android', - environment: 'production', - }); - }); - - it('should get the top 5 and the other option only', () => { - const deviceOptions = response.mostUsedCharts.find( - (chart) => chart.key === 'device' - )?.options; - expect(deviceOptions?.length).to.eql(6); - expect(deviceOptions?.find((option) => option.key === 'other')).to.not.be(undefined); - }); - - it('should get network connection type object from span events', () => { - const nctOptions = response.mostUsedCharts.find( - (chart) => chart.key === 'netConnectionType' - )?.options; - expect(nctOptions?.length).to.eql(2); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts deleted file mode 100644 index f7f3092935c3..000000000000 --- a/x-pack/test/apm_api_integration/tests/mobile/mobile_sessions_timeseries.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { ENVIRONMENT_ALL } from '@kbn/apm-plugin/common/environment_filter_values'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { generateMobileData } from './generate_mobile_data'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const start = new Date('2023-01-01T00:00:00.000Z').getTime(); - const end = new Date('2023-01-01T02:00:00.000Z').getTime(); - - async function getSessionsChart({ - environment = ENVIRONMENT_ALL.value, - kuery = '', - serviceName, - transactionType = 'mobile', - offset, - }: { - environment?: string; - kuery?: string; - serviceName: string; - transactionType?: string; - offset?: string; - }) { - return await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions', - params: { - path: { serviceName }, - query: { - environment, - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - offset, - kuery, - transactionType, - }, - }, - }); - } - - registry.when.skip('without data loaded', { config: 'basic', archives: [] }, () => { - describe('when no data', () => { - it('handles empty state', async () => { - const response = await getSessionsChart({ serviceName: 'foo' }); - expect(response.body.currentPeriod.timeseries).to.eql([]); - expect(response.body.previousPeriod.timeseries).to.eql([]); - expect(response.status).to.be(200); - }); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177393 - registry.when.skip('with data loaded', { config: 'basic', archives: [] }, () => { - before(async () => { - await generateMobileData({ - apmSynthtraceEsClient, - start, - end, - }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - describe('when data is loaded', () => { - it('returns timeseries for sessions chart', async () => { - const response = await getSessionsChart({ serviceName: 'synth-android', offset: '1d' }); - - expect(response.status).to.be(200); - expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( - true - ); - - expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); - expect(response.body.previousPeriod.timeseries[0].y).to.eql(0); - }); - - it('returns only current period timeseries when offset is not available', async () => { - const response = await getSessionsChart({ serviceName: 'synth-android' }); - - expect(response.status).to.be(200); - expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( - true - ); - - expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); - expect(response.body.previousPeriod.timeseries).to.eql([]); - }); - }); - - describe('when filters are applied', () => { - it('returns empty state for filters', async () => { - const response = await getSessionsChart({ - serviceName: 'synth-android', - environment: 'production', - kuery: `app.version:"none"`, - }); - - expect(response.body.currentPeriod.timeseries.every((item) => item.y === 0)).to.eql(true); - expect(response.body.previousPeriod.timeseries.every((item) => item.y === 0)).to.eql(true); - }); - - it('returns the correct values filter is applied', async () => { - const response = await getSessionsChart({ - serviceName: 'synth-android', - environment: 'production', - kuery: `transaction.name : "Start View - View Appearing"`, - }); - - expect(response.status).to.be(200); - expect(response.body.currentPeriod.timeseries.some((item) => item.x && item.y)).to.eql( - true - ); - - expect(response.body.currentPeriod.timeseries[0].y).to.eql(6); - expect(response.body.previousPeriod.timeseries).to.eql([]); - }); - }); - }); -} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 9db41aecbb61..2ba14ceb1218 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -187,6 +187,6 @@ "@kbn/alerting-types", "@kbn/ai-assistant-common", "@kbn/core-deprecations-common", - "@kbn/usage-collection-plugin", + "@kbn/usage-collection-plugin" ] } From d27f2412b7b1aa0d74b5cd9366d6d0d5646325cd Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Mon, 11 Nov 2024 16:30:35 +0100 Subject: [PATCH 011/100] [APM] Migrate `/cold_start` to deployment agnostic test (#199270) ## Summary Closes https://github.com/elastic/kibana/issues/198961 Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `cold_start` test folder to Deployment-agnostic testing strategy. ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` ## Checks - [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --- .../apm/cold_start/cold_start.spec.ts | 269 ++++++++++++++++++ .../cold_start_by_transaction_name.spec.ts | 95 +++++-- .../observability/apm/cold_start/index.ts | 15 + .../apis/observability/apm/index.ts | 1 + .../tests/cold_start/cold_start.spec.ts | 204 ------------- .../generate_data.ts | 64 ----- .../tests/cold_start/generate_data.ts | 70 ----- 7 files changed, 360 insertions(+), 358 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start.spec.ts rename x-pack/test/{apm_api_integration/tests/cold_start/cold_start_by_transaction_name => api_integration/deployment_agnostic/apis/observability/apm/cold_start}/cold_start_by_transaction_name.spec.ts (76%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/index.ts delete mode 100644 x-pack/test/apm_api_integration/tests/cold_start/cold_start.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts delete mode 100644 x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start.spec.ts new file mode 100644 index 000000000000..0e2c89aa4336 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start.spec.ts @@ -0,0 +1,269 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { first, last, uniq } from 'lodash'; +import moment from 'moment'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { + APIReturnType, + APIClientRequestParamsOf, +} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +type ColdStartRate = + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate'>; + +const dataConfig = { + serviceName: 'synth-go', + coldStartTransaction: { + name: 'GET /apple 🍎', + duration: 1000, + }, + warmStartTransaction: { + name: 'GET /banana 🍌', + duration: 2000, + }, +}; + +async function generateData({ + apmSynthtraceEsClient, + start, + end, + coldStartRate, + warmStartRate, +}: { + apmSynthtraceEsClient: ApmSynthtraceEsClient; + start: number; + end: number; + coldStartRate: number; + warmStartRate: number; +}) { + const { coldStartTransaction, warmStartTransaction, serviceName } = dataConfig; + const instance = apm + .service({ name: serviceName, environment: 'production', agentName: 'go' }) + .instance('instance-a'); + + const traceEvents = [ + timerange(start, end) + .interval('1m') + .rate(coldStartRate) + .generator((timestamp) => + instance + .transaction({ transactionName: coldStartTransaction.name }) + .defaults({ + 'faas.coldstart': true, + }) + .timestamp(timestamp) + .duration(coldStartTransaction.duration) + .success() + ), + timerange(start, end) + .interval('1m') + .rate(warmStartRate) + .generator((timestamp) => + instance + .transaction({ transactionName: warmStartTransaction.name }) + .defaults({ + 'faas.coldstart': false, + }) + .timestamp(timestamp) + .duration(warmStartTransaction.duration) + .success() + ), + ]; + + await apmSynthtraceEsClient.index(traceEvents); +} + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + const { serviceName } = dataConfig; + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + async function callApi( + overrides?: RecursivePartial< + APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate'>['params'] + > + ) { + return await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate', + params: { + path: { serviceName }, + query: { + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + kuery: '', + ...overrides?.query, + }, + }, + }); + } + + describe('Cold start', () => { + describe('Cold start rate when data is not loaded', () => { + it('handles empty state', async () => { + const { status, body } = await callApi(); + + expect(status).to.be(200); + expect(body.currentPeriod.transactionColdstartRate).to.empty(); + expect(body.currentPeriod.average).to.be(null); + + expect(body.previousPeriod.transactionColdstartRate).to.empty(); + expect(body.previousPeriod.average).to.be(null); + }); + }); + + describe('Cold start rate when data is generated', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + }); + + describe('without comparison', () => { + let body: ColdStartRate; + let status: number; + + before(async () => { + await generateData({ + apmSynthtraceEsClient, + start, + end, + coldStartRate: 10, + warmStartRate: 30, + }); + const response = await callApi(); + body = response.body; + status = response.status; + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('returns correct HTTP status', () => { + expect(status).to.be(200); + }); + + it('returns an array of transaction cold start rates', () => { + expect(body).to.have.property('currentPeriod'); + expect(body.currentPeriod.transactionColdstartRate).to.have.length(15); + expect(body.currentPeriod.transactionColdstartRate.every(({ y }) => y === 0.25)).to.be( + true + ); + }); + + it('returns correct average rate', () => { + expect(body.currentPeriod.average).to.be(0.25); + }); + + it("doesn't have data for the previous period", () => { + expect(body).to.have.property('previousPeriod'); + expect(body.previousPeriod.transactionColdstartRate).to.have.length(0); + expect(body.previousPeriod.average).to.be(null); + }); + }); + + describe('with comparison', () => { + let body: ColdStartRate; + let status: number; + + before(async () => { + const startDate = moment(start).add(6, 'minutes'); + const endDate = moment(start).add(9, 'minutes'); + const comparisonStartDate = new Date(start); + const comparisonEndDate = moment(start).add(3, 'minutes'); + + await generateData({ + apmSynthtraceEsClient, + start: startDate.valueOf(), + end: endDate.valueOf(), + coldStartRate: 10, + warmStartRate: 30, + }); + await generateData({ + apmSynthtraceEsClient, + start: comparisonStartDate.getTime(), + end: comparisonEndDate.valueOf(), + coldStartRate: 20, + warmStartRate: 20, + }); + + const response = await callApi({ + query: { + start: startDate.toISOString(), + end: endDate.subtract(1, 'seconds').toISOString(), + offset: '6m', + }, + }); + body = response.body; + status = response.status; + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('returns correct HTTP status', () => { + expect(status).to.be(200); + }); + + it('returns some data', () => { + expect(body.currentPeriod.average).not.to.be(null); + expect(body.currentPeriod.transactionColdstartRate.length).to.be.greaterThan(0); + const hasCurrentPeriodData = body.currentPeriod.transactionColdstartRate.some(({ y }) => + isFiniteNumber(y) + ); + expect(hasCurrentPeriodData).to.equal(true); + + expect(body.previousPeriod.average).not.to.be(null); + expect(body.previousPeriod.transactionColdstartRate.length).to.be.greaterThan(0); + const hasPreviousPeriodData = body.previousPeriod.transactionColdstartRate.some(({ y }) => + isFiniteNumber(y) + ); + expect(hasPreviousPeriodData).to.equal(true); + }); + + it('has same start time for both periods', () => { + expect(first(body.currentPeriod.transactionColdstartRate)?.x).to.equal( + first(body.previousPeriod.transactionColdstartRate)?.x + ); + }); + + it('has same end time for both periods', () => { + expect(last(body.currentPeriod.transactionColdstartRate)?.x).to.equal( + last(body.previousPeriod.transactionColdstartRate)?.x + ); + }); + + it('returns an array of transaction cold start rates', () => { + const currentValuesUnique = uniq( + body.currentPeriod.transactionColdstartRate.map(({ y }) => y) + ); + const prevValuesUnique = uniq( + body.previousPeriod.transactionColdstartRate.map(({ y }) => y) + ); + + expect(currentValuesUnique).to.eql([0.25]); + expect(body.currentPeriod.transactionColdstartRate).to.have.length(3); + + expect(prevValuesUnique).to.eql([0.5]); + expect(body.previousPeriod.transactionColdstartRate).to.have.length(3); + }); + + it('has same average value for both periods', () => { + expect(body.currentPeriod.average).to.be(0.25); + expect(body.previousPeriod.average).to.be(0.5); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/cold_start_by_transaction_name.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start_by_transaction_name.spec.ts similarity index 76% rename from x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/cold_start_by_transaction_name.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start_by_transaction_name.spec.ts index 7019735a0549..a2aa3a7d1154 100644 --- a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/cold_start_by_transaction_name.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/cold_start_by_transaction_name.spec.ts @@ -7,22 +7,78 @@ import expect from '@kbn/expect'; import { first, last } from 'lodash'; import moment from 'moment'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { APIReturnType, APIClientRequestParamsOf, } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; -import { dataConfig, generateData } from './generate_data'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; type ColdStartRate = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name'>; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +const dataConfig = { + serviceName: 'synth-go', + transactionName: 'GET /apple 🍎', + duration: 1000, +}; + +async function generateData({ + apmSynthtraceEsClient, + start, + end, + coldStartRate, + warmStartRate, +}: { + apmSynthtraceEsClient: ApmSynthtraceEsClient; + start: number; + end: number; + coldStartRate: number; + warmStartRate: number; +}) { + const { transactionName, duration, serviceName } = dataConfig; + const instance = apm + .service({ name: serviceName, environment: 'production', agentName: 'go' }) + .instance('instance-a'); + + const traceEvents = [ + timerange(start, end) + .interval('1m') + .rate(coldStartRate) + .generator((timestamp) => + instance + .transaction({ transactionName }) + .defaults({ + 'faas.coldstart': true, + }) + .timestamp(timestamp) + .duration(duration) + .success() + ), + timerange(start, end) + .interval('1m') + .rate(warmStartRate) + .generator((timestamp) => + instance + .transaction({ transactionName }) + .defaults({ + 'faas.coldstart': false, + }) + .timestamp(timestamp) + .duration(duration) + .success() + ), + ]; + + await apmSynthtraceEsClient.index(traceEvents); +} + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const { serviceName, transactionName } = dataConfig; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -51,10 +107,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); } - registry.when( - 'Cold start rate by transaction name when data is not loaded', - { config: 'basic', archives: [] }, - () => { + describe('Cold start by transaction name', () => { + describe('when data is not loaded', () => { it('handles empty state', async () => { const { status, body } = await callApi(); @@ -65,14 +119,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(body.previousPeriod.transactionColdstartRate).to.empty(); expect(body.previousPeriod.average).to.be(null); }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177616 - registry.when( - 'Cold start rate by transaction name when data is generated', - { config: 'basic', archives: [] }, - () => { + }); + + describe('when data is generated', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + }); + describe('without comparison', () => { let body: ColdStartRate; let status: number; @@ -202,6 +257,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(body.previousPeriod.average).to.be(0.5); }); }); - } - ); + }); + }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/index.ts new file mode 100644 index 000000000000..a5d8045f227d --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/cold_start/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('cold_start', () => { + loadTestFile(require.resolve('./cold_start.spec.ts')); + loadTestFile(require.resolve('./cold_start_by_transaction_name.spec.ts')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index 93ef6ed2764f..3e490a621d3f 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -15,5 +15,6 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); + loadTestFile(require.resolve('./cold_start')); }); } diff --git a/x-pack/test/apm_api_integration/tests/cold_start/cold_start.spec.ts b/x-pack/test/apm_api_integration/tests/cold_start/cold_start.spec.ts deleted file mode 100644 index 7d0b2f6d8a62..000000000000 --- a/x-pack/test/apm_api_integration/tests/cold_start/cold_start.spec.ts +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { first, last, uniq } from 'lodash'; -import moment from 'moment'; -import { - APIReturnType, - APIClientRequestParamsOf, -} from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; -import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; -import { dataConfig, generateData } from './generate_data'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -type ColdStartRate = - APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate'>; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - const { serviceName } = dataConfig; - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - async function callApi( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate'>['params'] - > - ) { - return await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate', - params: { - path: { serviceName }, - query: { - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - kuery: '', - ...overrides?.query, - }, - }, - }); - } - - registry.when( - 'Cold start rate when data is not loaded', - { config: 'basic', archives: [] }, - () => { - it('handles empty state', async () => { - const { status, body } = await callApi(); - - expect(status).to.be(200); - expect(body.currentPeriod.transactionColdstartRate).to.empty(); - expect(body.currentPeriod.average).to.be(null); - - expect(body.previousPeriod.transactionColdstartRate).to.empty(); - expect(body.previousPeriod.average).to.be(null); - }); - } - ); - - // FLAKY: https://github.com/elastic/kibana/issues/177113 - registry.when('Cold start rate when data is generated', { config: 'basic', archives: [] }, () => { - describe('without comparison', () => { - let body: ColdStartRate; - let status: number; - - before(async () => { - await generateData({ - apmSynthtraceEsClient, - start, - end, - coldStartRate: 10, - warmStartRate: 30, - }); - const response = await callApi(); - body = response.body; - status = response.status; - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('returns correct HTTP status', () => { - expect(status).to.be(200); - }); - - it('returns an array of transaction cold start rates', () => { - expect(body).to.have.property('currentPeriod'); - expect(body.currentPeriod.transactionColdstartRate).to.have.length(15); - expect(body.currentPeriod.transactionColdstartRate.every(({ y }) => y === 0.25)).to.be( - true - ); - }); - - it('returns correct average rate', () => { - expect(body.currentPeriod.average).to.be(0.25); - }); - - it("doesn't have data for the previous period", () => { - expect(body).to.have.property('previousPeriod'); - expect(body.previousPeriod.transactionColdstartRate).to.have.length(0); - expect(body.previousPeriod.average).to.be(null); - }); - }); - - describe('with comparison', () => { - let body: ColdStartRate; - let status: number; - - before(async () => { - const startDate = moment(start).add(6, 'minutes'); - const endDate = moment(start).add(9, 'minutes'); - const comparisonStartDate = new Date(start); - const comparisonEndDate = moment(start).add(3, 'minutes'); - - await generateData({ - apmSynthtraceEsClient, - start: startDate.valueOf(), - end: endDate.valueOf(), - coldStartRate: 10, - warmStartRate: 30, - }); - await generateData({ - apmSynthtraceEsClient, - start: comparisonStartDate.getTime(), - end: comparisonEndDate.valueOf(), - coldStartRate: 20, - warmStartRate: 20, - }); - - const response = await callApi({ - query: { - start: startDate.toISOString(), - end: endDate.subtract(1, 'seconds').toISOString(), - offset: '6m', - }, - }); - body = response.body; - status = response.status; - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('returns correct HTTP status', () => { - expect(status).to.be(200); - }); - - it('returns some data', () => { - expect(body.currentPeriod.average).not.to.be(null); - expect(body.currentPeriod.transactionColdstartRate.length).to.be.greaterThan(0); - const hasCurrentPeriodData = body.currentPeriod.transactionColdstartRate.some(({ y }) => - isFiniteNumber(y) - ); - expect(hasCurrentPeriodData).to.equal(true); - - expect(body.previousPeriod.average).not.to.be(null); - expect(body.previousPeriod.transactionColdstartRate.length).to.be.greaterThan(0); - const hasPreviousPeriodData = body.previousPeriod.transactionColdstartRate.some(({ y }) => - isFiniteNumber(y) - ); - expect(hasPreviousPeriodData).to.equal(true); - }); - - it('has same start time for both periods', () => { - expect(first(body.currentPeriod.transactionColdstartRate)?.x).to.equal( - first(body.previousPeriod.transactionColdstartRate)?.x - ); - }); - - it('has same end time for both periods', () => { - expect(last(body.currentPeriod.transactionColdstartRate)?.x).to.equal( - last(body.previousPeriod.transactionColdstartRate)?.x - ); - }); - - it('returns an array of transaction cold start rates', () => { - const currentValuesUnique = uniq( - body.currentPeriod.transactionColdstartRate.map(({ y }) => y) - ); - const prevValuesUnique = uniq( - body.previousPeriod.transactionColdstartRate.map(({ y }) => y) - ); - - expect(currentValuesUnique).to.eql([0.25]); - expect(body.currentPeriod.transactionColdstartRate).to.have.length(3); - - expect(prevValuesUnique).to.eql([0.5]); - expect(body.previousPeriod.transactionColdstartRate).to.have.length(3); - }); - - it('has same average value for both periods', () => { - expect(body.currentPeriod.average).to.be(0.25); - expect(body.previousPeriod.average).to.be(0.5); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts b/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts deleted file mode 100644 index ff4d725a3d01..000000000000 --- a/x-pack/test/apm_api_integration/tests/cold_start/cold_start_by_transaction_name/generate_data.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; - -export const dataConfig = { - serviceName: 'synth-go', - transactionName: 'GET /apple 🍎', - duration: 1000, -}; - -export async function generateData({ - apmSynthtraceEsClient, - start, - end, - coldStartRate, - warmStartRate, -}: { - apmSynthtraceEsClient: ApmSynthtraceEsClient; - start: number; - end: number; - coldStartRate: number; - warmStartRate: number; -}) { - const { transactionName, duration, serviceName } = dataConfig; - const instance = apm - .service({ name: serviceName, environment: 'production', agentName: 'go' }) - .instance('instance-a'); - - const traceEvents = [ - timerange(start, end) - .interval('1m') - .rate(coldStartRate) - .generator((timestamp) => - instance - .transaction({ transactionName }) - .defaults({ - 'faas.coldstart': true, - }) - .timestamp(timestamp) - .duration(duration) - .success() - ), - timerange(start, end) - .interval('1m') - .rate(warmStartRate) - .generator((timestamp) => - instance - .transaction({ transactionName }) - .defaults({ - 'faas.coldstart': false, - }) - .timestamp(timestamp) - .duration(duration) - .success() - ), - ]; - - await apmSynthtraceEsClient.index(traceEvents); -} diff --git a/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts b/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts deleted file mode 100644 index 9c36e53e8263..000000000000 --- a/x-pack/test/apm_api_integration/tests/cold_start/generate_data.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; - -export const dataConfig = { - serviceName: 'synth-go', - coldStartTransaction: { - name: 'GET /apple 🍎', - duration: 1000, - }, - warmStartTransaction: { - name: 'GET /banana 🍌', - duration: 2000, - }, -}; - -export async function generateData({ - apmSynthtraceEsClient, - start, - end, - coldStartRate, - warmStartRate, -}: { - apmSynthtraceEsClient: ApmSynthtraceEsClient; - start: number; - end: number; - coldStartRate: number; - warmStartRate: number; -}) { - const { coldStartTransaction, warmStartTransaction, serviceName } = dataConfig; - const instance = apm - .service({ name: serviceName, environment: 'production', agentName: 'go' }) - .instance('instance-a'); - - const traceEvents = [ - timerange(start, end) - .interval('1m') - .rate(coldStartRate) - .generator((timestamp) => - instance - .transaction({ transactionName: coldStartTransaction.name }) - .defaults({ - 'faas.coldstart': true, - }) - .timestamp(timestamp) - .duration(coldStartTransaction.duration) - .success() - ), - timerange(start, end) - .interval('1m') - .rate(warmStartRate) - .generator((timestamp) => - instance - .transaction({ transactionName: warmStartTransaction.name }) - .defaults({ - 'faas.coldstart': false, - }) - .timestamp(timestamp) - .duration(warmStartTransaction.duration) - .success() - ), - ]; - - await apmSynthtraceEsClient.index(traceEvents); -} From aa4f430ed59694b7f8c3d5b318c76e6aef95331e Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 11 Nov 2024 16:38:01 +0100 Subject: [PATCH 012/100] [ML] Data Visualizer: Optimize initial load bundles. (#199029) ## Summary Part of https://github.com/elastic/kibana/issues/194171. The `dataVisualizer` plugin loads quite some bundle chunks on the first Kibana load. On `main` it currently looks like this on page load: ![CleanShot 2024-11-05 at 17 51 35@2x](https://github.com/user-attachments/assets/7b2fcc7e-08fb-48b8-a1d9-03063b555e38) This PR refactors how embeddables and UI actions get initialized to avoid loading any additional async bundles on page load. ![CleanShot 2024-11-06 at 10 51 36@2x](https://github.com/user-attachments/assets/019c3c79-49b5-4e63-9b94-3d7b02963a9b) ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) --- packages/kbn-optimizer/limits.yml | 2 +- .../ui_actions/create_field_stats_table.tsx | 38 ++++++++++++++----- .../index_data_visualizer/ui_actions/index.ts | 7 ++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index b0357853720c..df8a077e844f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -33,7 +33,7 @@ pageLoadAssetSize: dataViewFieldEditor: 42021 dataViewManagement: 5300 dataViews: 65000 - dataVisualizer: 27530 + dataVisualizer: 30000 devTools: 38637 discover: 99999 discoverEnhanced: 42730 diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/create_field_stats_table.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/create_field_stats_table.tsx index 2c1254732c24..6b788e4acab4 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/create_field_stats_table.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/create_field_stats_table.tsx @@ -5,29 +5,22 @@ * 2.0. */ +import React from 'react'; + import { i18n } from '@kbn/i18n'; import type { PresentationContainer } from '@kbn/presentation-containers'; -import { tracksOverlays } from '@kbn/presentation-containers'; import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; -import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; import type { CoreStart } from '@kbn/core-lifecycle-browser'; -import { toMountPoint } from '@kbn/react-kibana-mount'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import React from 'react'; -import { isDefined } from '@kbn/ml-is-defined'; import { COMMON_VISUALIZATION_GROUPING } from '@kbn/visualizations-plugin/public'; import { ENABLE_ESQL } from '@kbn/esql-utils'; -import { FIELD_STATS_EMBEDDABLE_TYPE } from '../embeddables/field_stats/constants'; + import type { DataVisualizerStartDependencies } from '../../common/types/data_visualizer_plugin'; import type { FieldStatisticsTableEmbeddableApi, FieldStatsControlsApi, } from '../embeddables/field_stats/types'; -import { FieldStatsInitializerViewType } from '../embeddables/grid_embeddable/types'; import type { FieldStatsInitialState } from '../embeddables/grid_embeddable/types'; -import { getOrCreateDataViewByIndexPattern } from '../search_strategy/requests/get_data_view_by_index_pattern'; -import { FieldStatisticsInitializer } from '../embeddables/field_stats/field_stats_initializer'; const parentApiIsCompatible = async ( parentApi: unknown @@ -57,6 +50,21 @@ async function updatePanelFromFlyoutEdits({ initialState: FieldStatsInitialState; fieldStatsControlsApi?: FieldStatsControlsApi; }) { + const [ + { getOrCreateDataViewByIndexPattern }, + { FieldStatisticsInitializer }, + { tracksOverlays }, + { toMountPoint }, + { KibanaContextProvider }, + { isDefined }, + ] = await Promise.all([ + import('../search_strategy/requests/get_data_view_by_index_pattern'), + import('../embeddables/field_stats/field_stats_initializer'), + import('@kbn/presentation-containers'), + import('@kbn/react-kibana-mount'), + import('@kbn/kibana-react-plugin/public'), + import('@kbn/ml-is-defined'), + ]); const parentApi = api.parentApi; const overlayTracker = tracksOverlays(parentApi) ? parentApi : undefined; const services = { @@ -148,6 +156,16 @@ export function createAddFieldStatsTableAction( ); }, async execute(context) { + const [ + { IncompatibleActionError }, + { FIELD_STATS_EMBEDDABLE_TYPE }, + { FieldStatsInitializerViewType }, + ] = await Promise.all([ + import('@kbn/ui-actions-plugin/public'), + import('../embeddables/field_stats/constants'), + import('../embeddables/grid_embeddable/types'), + ]); + const presentationContainerParent = await parentApiIsCompatible(context.embeddable); if (!presentationContainerParent) throw new IncompatibleActionError(); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts index faa8f34bdfbd..ac2f73860e4f 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts @@ -8,14 +8,13 @@ import type { CoreStart } from '@kbn/core-lifecycle-browser'; import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; import type { DataVisualizerStartDependencies } from '../../common/types/data_visualizer_plugin'; +import { createAddFieldStatsTableAction } from './create_field_stats_table'; export function registerDataVisualizerUiActions( uiActions: UiActionsSetup, coreStart: CoreStart, pluginStart: DataVisualizerStartDependencies ) { - import('./create_field_stats_table').then(({ createAddFieldStatsTableAction }) => { - const addFieldStatsAction = createAddFieldStatsTableAction(coreStart, pluginStart); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addFieldStatsAction); - }); + const addFieldStatsAction = createAddFieldStatsTableAction(coreStart, pluginStart); + uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addFieldStatsAction); } From 53314895c268ffe5ab2a5c4c7f603e2e554471b8 Mon Sep 17 00:00:00 2001 From: mohamedhamed-ahmed Date: Mon, 11 Nov 2024 16:30:52 +0000 Subject: [PATCH 013/100] [Dataset Quality] Fix privileges test timeout (#199657) closes https://github.com/elastic/kibana/issues/198865 --- .../dataset_quality_privileges.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/x-pack/test/functional/apps/dataset_quality/dataset_quality_privileges.ts b/x-pack/test/functional/apps/dataset_quality/dataset_quality_privileges.ts index e196f92c1cf1..48d7216c33d5 100644 --- a/x-pack/test/functional/apps/dataset_quality/dataset_quality_privileges.ts +++ b/x-pack/test/functional/apps/dataset_quality/dataset_quality_privileges.ts @@ -20,6 +20,8 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid const security = getService('security'); const synthtrace = getService('logSynthtraceEsClient'); const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const to = new Date(new Date().setDate(new Date().getDate() - 1)).toISOString(); const apacheAccessDatasetName = 'apache.access'; @@ -144,15 +146,16 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid const datasetWithMonitorPrivilege = apacheAccessDatasetHumanName; const datasetWithoutMonitorPrivilege = 'synth.1'; - // "Size" should be available for `apacheAccessDatasetName` - await testSubjects.missingOrFail( - `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityInsufficientPrivileges}-sizeBytes-${datasetWithMonitorPrivilege}` - ); - - // "Size" should not be available for `datasetWithoutMonitorPrivilege` - await testSubjects.existOrFail( - `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityInsufficientPrivileges}-sizeBytes-${datasetWithoutMonitorPrivilege}` - ); + await retry.tryForTime(5000, async () => { + // "Size" should be available for `apacheAccessDatasetName` + await testSubjects.missingOrFail( + `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityInsufficientPrivileges}-sizeBytes-${datasetWithMonitorPrivilege}` + ); + // "Size" should not be available for `datasetWithoutMonitorPrivilege` + await testSubjects.existOrFail( + `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityInsufficientPrivileges}-sizeBytes-${datasetWithoutMonitorPrivilege}` + ); + }); }); it('Details page shows insufficient privileges warning for underprivileged data stream', async () => { From 750e578f2867d1ad6bf2f1f7506a1d75f2d77d05 Mon Sep 17 00:00:00 2001 From: Oyelola Victoria <123843734+VriaA@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:48:23 +0100 Subject: [PATCH 014/100] [lens] Prevent identical include and exclude values (#197628) ## Summary Fixes #194639 - This PR adds filtering logic to: - `onChangeIncludeExcludeOptions` - `onCreateOption` - `onIncludeRegexChangeToDebounce` - `onExcludeRegexChangeToDebounce` These changes prevent the include and exclude fields from having the same values simultaneously. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios https://github.com/user-attachments/assets/8f848c2a-0bea-46c6-b335-b04d62c12aef --------- Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../terms/include_exclude_options.test.tsx | 166 +++++++++++++++++- .../terms/include_exclude_options.tsx | 88 +++++----- 2 files changed, 212 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.test.tsx index 87026e061329..8df04b23a543 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { fireEvent, render, screen, within } from '@testing-library/react'; +import { fireEvent, render, screen, within, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { IncludeExcludeRow, IncludeExcludeRowProps } from './include_exclude_options'; @@ -152,4 +152,168 @@ describe('IncludeExcludeComponent', () => { }); expect(onUpdateSpy).toHaveBeenCalledTimes(2); }); + + it('should prevent identical include and exclude values on change when making single selections', async () => { + renderIncludeExcludeRow({ + include: undefined, + exclude: undefined, + isNumberField: false, + tableRows, + }); + + await userEvent.click(screen.getByRole('combobox', { name: 'Include values' })); + await userEvent.click(screen.getByRole('option', { name: 'ABC' })); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('ABC'); + + await userEvent.click(screen.getByRole('combobox', { name: 'Exclude values' })); + await userEvent.click(screen.getByRole('option', { name: 'ABC' })); + expect(screen.getByTestId('lens-exclude-terms-combobox')).toHaveTextContent('ABC'); + + expect(screen.getByTestId('lens-include-terms-combobox')).not.toHaveTextContent('ABC'); + + expect(onUpdateSpy).toHaveBeenCalledTimes(3); + }); + + it('should prevent identical include and exclude values on change when making multiple selections', async () => { + renderIncludeExcludeRow({ + include: undefined, + exclude: undefined, + isNumberField: false, + tableRows, + }); + + await userEvent.click(screen.getByRole('combobox', { name: 'Include values' })); + await userEvent.click(screen.getByRole('option', { name: 'ABC' })); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('ABC'); + + await userEvent.click(screen.getByRole('combobox', { name: 'Include values' })); + await userEvent.click(screen.getByRole('option', { name: 'FEF' })); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('FEF'); + + await userEvent.click(screen.getByRole('combobox', { name: 'Exclude values' })); + await userEvent.click(screen.getByRole('option', { name: 'ABC' })); + expect(screen.getByTestId('lens-include-terms-combobox')).not.toHaveTextContent('ABC'); + + expect(screen.getByTestId('lens-exclude-terms-combobox')).toHaveTextContent('ABC'); + + expect(onUpdateSpy).toHaveBeenCalledTimes(4); + }); + + it('should prevent identical include and exclude values on create option', async () => { + renderIncludeExcludeRow({ + include: undefined, + exclude: undefined, + isNumberField: false, + tableRows, + }); + + await userEvent.click(screen.getByRole('combobox', { name: 'Include values' })); + await userEvent.type(screen.getByRole('combobox', { name: 'Include values' }), 'test{enter}'); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('test'); + + await userEvent.click(screen.getByRole('combobox', { name: 'Exclude values' })); + await userEvent.type(screen.getByRole('combobox', { name: 'Exclude values' }), 'test{enter}'); + expect(screen.getByTestId('lens-exclude-terms-combobox')).toHaveTextContent('test'); + + expect(screen.getByTestId('lens-include-terms-combobox')).not.toHaveTextContent('test'); + + expect(onUpdateSpy).toHaveBeenCalledTimes(3); + }); + + it('should prevent identical include and exclude values when creating multiple options', async () => { + renderIncludeExcludeRow({ + include: undefined, + exclude: undefined, + isNumberField: false, + tableRows, + }); + + await userEvent.click(screen.getByRole('combobox', { name: 'Include values' })); + await userEvent.type(screen.getByRole('combobox', { name: 'Include values' }), 'test{enter}'); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('test'); + + await userEvent.type(screen.getByRole('combobox', { name: 'Include values' }), 'test1{enter}'); + expect(screen.getByTestId('lens-include-terms-combobox')).toHaveTextContent('test1'); + + await userEvent.click(screen.getByRole('combobox', { name: 'Exclude values' })); + await userEvent.type(screen.getByRole('combobox', { name: 'Exclude values' }), 'test1{enter}'); + expect(screen.getByTestId('lens-exclude-terms-combobox')).toHaveTextContent('test1'); + + expect(screen.getByTestId('lens-include-terms-combobox')).not.toHaveTextContent('test1'); + + expect(onUpdateSpy).toHaveBeenCalledTimes(4); + }); + + it('should prevent identical include value on exclude regex value change', async () => { + jest.useFakeTimers(); + + renderIncludeExcludeRow({ + include: [''], + exclude: [''], + includeIsRegex: true, + excludeIsRegex: true, + tableRows, + }); + + const includeRegexInput = screen.getByTestId('lens-include-terms-regex-input'); + const excludeRegexInput = screen.getByTestId('lens-exclude-terms-regex-input'); + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + + await user.type(includeRegexInput, 'test.*'); + act(() => { + jest.advanceTimersByTime(256); + }); + expect(includeRegexInput).toHaveValue('test.*'); + expect(onUpdateSpy).toHaveBeenCalledWith('include', ['test.*'], 'includeIsRegex', true); + + await user.type(excludeRegexInput, 'test.*'); + act(() => { + jest.advanceTimersByTime(256); + }); + expect(excludeRegexInput).toHaveValue('test.*'); + expect(onUpdateSpy).toHaveBeenCalledWith('exclude', ['test.*'], 'excludeIsRegex', true); + + expect(includeRegexInput).toHaveValue(''); + expect(onUpdateSpy).toHaveBeenCalledWith('include', [''], 'includeIsRegex', true); + + expect(onUpdateSpy).toHaveBeenCalledTimes(3); + + jest.useRealTimers(); + }); + + it('should prevent identical exclude value on include regex value change', async () => { + jest.useFakeTimers(); + + renderIncludeExcludeRow({ + include: [''], + exclude: [''], + includeIsRegex: true, + excludeIsRegex: true, + tableRows, + }); + + const includeRegexInput = screen.getByTestId('lens-include-terms-regex-input'); + const excludeRegexInput = screen.getByTestId('lens-exclude-terms-regex-input'); + const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime }); + + await user.type(excludeRegexInput, 'test.*'); + act(() => { + jest.advanceTimersByTime(256); + }); + expect(excludeRegexInput).toHaveValue('test.*'); + expect(onUpdateSpy).toHaveBeenCalledWith('exclude', ['test.*'], 'excludeIsRegex', true); + + await user.type(includeRegexInput, 'test.*'); + act(() => { + jest.advanceTimersByTime(256); + }); + expect(includeRegexInput).toHaveValue('test.*'); + expect(onUpdateSpy).toHaveBeenCalledWith('include', ['test.*'], 'includeIsRegex', true); + + expect(excludeRegexInput).toHaveValue(''); + expect(onUpdateSpy).toHaveBeenCalledWith('exclude', [''], 'excludeIsRegex', true); + + expect(onUpdateSpy).toHaveBeenCalledTimes(3); + jest.useRealTimers(); + }); }); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.tsx index 41f521088af9..b2a8abb62c1a 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/terms/include_exclude_options.tsx @@ -99,68 +99,77 @@ export const IncludeExcludeRow = ({ selectedOptions: IncludeExcludeOptions[], operation: 'include' | 'exclude' ) => { - const options = { - ...includeExcludeSelectedOptions, - [operation]: selectedOptions, - }; - setIncludeExcludeSelectedOptions(options); - const terms = selectedOptions.map((option) => { - if (!Number.isNaN(Number(option.label))) { - return Number(option.label); - } - return option.label; + const otherOperation = operation === 'include' ? 'exclude' : 'include'; + const otherSelectedOptions = includeExcludeSelectedOptions[otherOperation] ?? []; + const hasIdenticalOptions = selectedOptions.some((option) => { + return otherSelectedOptions.some((otherOption) => otherOption.label === option.label); }); - const param = `${operation}IsRegex`; - updateParams(operation, terms, param, false); - }; - - const onCreateOption = ( - searchValue: string, - flattenedOptions: IncludeExcludeOptions[] = [], - operation: 'include' | 'exclude' - ) => { - const newOption = { - label: searchValue, - }; - let includeExcludeOptions = []; + const otherSelectedNonIdenticalOptions = hasIdenticalOptions + ? otherSelectedOptions.filter( + (otherOption) => !selectedOptions.some((option) => option.label === otherOption.label) + ) + : otherSelectedOptions; - const includeORExcludeSelectedOptions = includeExcludeSelectedOptions[operation] ?? []; - includeExcludeOptions = [...includeORExcludeSelectedOptions, newOption]; const options = { - ...includeExcludeSelectedOptions, - [operation]: includeExcludeOptions, + [otherOperation]: otherSelectedNonIdenticalOptions, + [operation]: selectedOptions, }; setIncludeExcludeSelectedOptions(options); - const terms = includeExcludeOptions.map((option) => { - if (!Number.isNaN(Number(option.label))) { - return Number(option.label); - } - return option.label; - }); + const getTerms = (updatedSelectedOptions: IncludeExcludeOptions[]) => + updatedSelectedOptions.map((option) => { + if (!Number.isNaN(Number(option.label))) { + return Number(option.label); + } + return option.label; + }); + + const terms = getTerms(selectedOptions); const param = `${operation}IsRegex`; updateParams(operation, terms, param, false); + + if (hasIdenticalOptions) { + const otherTerms = getTerms(otherSelectedNonIdenticalOptions); + const otherParam = `${otherOperation}IsRegex`; + updateParams(otherOperation, otherTerms, otherParam, false); + } + }; + + const onCreateOption = (searchValue: string, operation: 'include' | 'exclude') => { + const newOption = { label: searchValue }; + const selectedOptions = [...(includeExcludeSelectedOptions[operation] ?? []), newOption]; + onChangeIncludeExcludeOptions(selectedOptions, operation); }; const onIncludeRegexChangeToDebounce = useCallback( (newIncludeValue: string | number | undefined) => { + const isEqualToExcludeValue = newIncludeValue === regex.exclude; + const excludeValue = isEqualToExcludeValue ? '' : regex.exclude; setRegex({ - ...regex, + exclude: excludeValue, include: newIncludeValue, }); updateParams('include', [newIncludeValue ?? ''], 'includeIsRegex', true); + if (isEqualToExcludeValue) { + updateParams('exclude', [''], 'excludeIsRegex', true); + } }, [regex, updateParams] ); const onExcludeRegexChangeToDebounce = useCallback( (newExcludeValue: string | number | undefined) => { + const isEqualToIncludeValue = newExcludeValue === regex.include; + const includeValue = isEqualToIncludeValue ? '' : regex.include; setRegex({ - ...regex, + include: includeValue, exclude: newExcludeValue, }); updateParams('exclude', [newExcludeValue ?? ''], 'excludeIsRegex', true); + if (isEqualToIncludeValue) { + updateParams('include', [''], 'includeIsRegex', true); + } }, [regex, updateParams] ); @@ -247,9 +256,7 @@ export const IncludeExcludeRow = ({ options={termsOptions} selectedOptions={includeExcludeSelectedOptions.include} onChange={(options) => onChangeIncludeExcludeOptions(options, 'include')} - onCreateOption={(searchValue, options) => - onCreateOption(searchValue, options, 'include') - } + onCreateOption={(searchValue) => onCreateOption(searchValue, 'include')} isClearable={true} data-test-subj="lens-include-terms-combobox" autoFocus @@ -300,6 +307,7 @@ export const IncludeExcludeRow = ({ defaultMessage: 'Enter a regex to filter values', } )} + data-test-subj="lens-exclude-terms-regex-input" value={excludeRegexValue} onChange={(e) => { onExcludeRegexValueChange(e.target.value); @@ -322,9 +330,7 @@ export const IncludeExcludeRow = ({ options={termsOptions} selectedOptions={includeExcludeSelectedOptions.exclude} onChange={(options) => onChangeIncludeExcludeOptions(options, 'exclude')} - onCreateOption={(searchValue, options) => - onCreateOption(searchValue, options, 'exclude') - } + onCreateOption={(searchValue) => onCreateOption(searchValue, 'exclude')} isClearable={true} data-test-subj="lens-exclude-terms-combobox" autoFocus From c611e52309983ceaf1bd4cfafb612efcde253dc3 Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Mon, 11 Nov 2024 18:23:46 +0100 Subject: [PATCH 015/100] [APM] Migrate `/entities` to deployment agnostic test (#199579) ## Summary Closes https://github.com/elastic/kibana/issues/198968 Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `entities` test folder to Deployment-agnostic testing strategy. ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` ## Checks - [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --- .../apis/observability/apm/entities/index.ts | 15 ++ ...service_logs_error_rate_timeseries.spec.ts | 47 ++--- .../service_logs_rate_timeseries.spec.ts | 180 ++++++++++++++++++ .../apis/observability/apm/index.ts | 1 + .../logs/service_logs_rate_timeseries.spec.ts | 173 ----------------- 5 files changed, 220 insertions(+), 196 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/index.ts rename x-pack/test/{apm_api_integration/tests/entities/logs => api_integration/deployment_agnostic/apis/observability/apm/entities}/service_logs_error_rate_timeseries.spec.ts (85%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_rate_timeseries.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/entities/logs/service_logs_rate_timeseries.spec.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/index.ts new file mode 100644 index 000000000000..d7a36e3e447b --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('entities', () => { + loadTestFile(require.resolve('./service_logs_error_rate_timeseries.spec.ts')); + loadTestFile(require.resolve('./service_logs_rate_timeseries.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/entities/logs/service_logs_error_rate_timeseries.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_error_rate_timeseries.spec.ts similarity index 85% rename from x-pack/test/apm_api_integration/tests/entities/logs/service_logs_error_rate_timeseries.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_error_rate_timeseries.spec.ts index 282039b8957c..f6e167db0318 100644 --- a/x-pack/test/apm_api_integration/tests/entities/logs/service_logs_error_rate_timeseries.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_error_rate_timeseries.spec.ts @@ -9,12 +9,12 @@ import { log, timerange } from '@kbn/apm-synthtrace-client'; import { first, last } from 'lodash'; import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; import { APIClientRequestParamsOf } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { LogsSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const logSynthtrace = getService('logSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const serviceName = 'synth-go'; const start = new Date('2024-01-01T00:00:00.000Z').getTime(); @@ -45,25 +45,26 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); return response; } + describe('logs error rate timeseries', () => { + describe('when data is not loaded', () => { + it('handles empty state', async () => { + const response = await getLogsErrorRateTimeseries(); + expect(response.status).to.be(200); + expect(response.body.currentPeriod).to.empty(); + }); + }); - registry.when( - 'Logs error rate timeseries when data is not loaded', - { config: 'basic', archives: [] }, - () => { - describe('Logs error rate api', () => { - it('handles the empty state', async () => { - const response = await getLogsErrorRateTimeseries(); - expect(response.status).to.be(200); - expect(response.body.currentPeriod).to.empty(); - }); + describe('when data loaded', () => { + let logSynthtrace: LogsSynthtraceEsClient; + + before(async () => { + logSynthtrace = await synthtrace.createLogsSynthtraceEsClient(); + }); + + after(async () => { + await logSynthtrace.clean(); }); - } - ); - registry.when( - 'Logs error rate timeseries when data loaded', - { config: 'basic', archives: [] }, - () => { describe('Logs without log level field', () => { before(async () => { return logSynthtrace.index([ @@ -170,6 +171,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); }); - } - ); + }); + }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_rate_timeseries.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_rate_timeseries.spec.ts new file mode 100644 index 000000000000..fb10925b9906 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/entities/service_logs_rate_timeseries.spec.ts @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from '@kbn/expect'; +import { log, timerange } from '@kbn/apm-synthtrace-client'; +import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import { APIClientRequestParamsOf } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; +import { first, last } from 'lodash'; +import { LogsSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + const serviceName = 'synth-go'; + const start = new Date('2024-01-01T00:00:00.000Z').getTime(); + const end = new Date('2024-01-01T00:15:00.000Z').getTime() - 1; + + const hostName = 'synth-host'; + + async function getLogsRateTimeseries( + overrides?: RecursivePartial< + APIClientRequestParamsOf<'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries'>['params'] + > + ) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries', + params: { + path: { + serviceName: 'synth-go', + ...overrides?.path, + }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + ...overrides?.query, + }, + }, + }); + return response; + } + describe('logs rate timeseries', () => { + describe('when data is not loaded', () => { + it('handles empty state', async () => { + const response = await getLogsRateTimeseries(); + expect(response.status).to.be(200); + expect(response.body.currentPeriod).to.empty(); + }); + }); + + describe('when data loaded', () => { + let logSynthtrace: LogsSynthtraceEsClient; + + before(async () => { + logSynthtrace = await synthtrace.createLogsSynthtraceEsClient(); + }); + + after(async () => { + await logSynthtrace.clean(); + }); + + describe('Logs without log level field', () => { + before(async () => { + return logSynthtrace.index([ + timerange(start, end) + .interval('1m') + .rate(1) + .generator((timestamp) => + log.create().message('This is a log message').timestamp(timestamp).defaults({ + 'log.file.path': '/my-service.log', + 'service.name': serviceName, + 'host.name': hostName, + }) + ), + ]); + }); + after(async () => { + await logSynthtrace.clean(); + }); + + it('returns {} if log level is not available ', async () => { + const response = await getLogsRateTimeseries(); + expect(response.status).to.be(200); + }); + }); + + describe('Logs with log.level=error', () => { + before(async () => { + return logSynthtrace.index([ + timerange(start, end) + .interval('1m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is a log message') + .logLevel('error') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': serviceName, + 'host.name': hostName, + 'service.environment': 'test', + }) + ), + timerange(start, end) + .interval('2m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is an error log message') + .logLevel('error') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': 'my-service', + 'host.name': hostName, + 'service.environment': 'production', + }) + ), + timerange(start, end) + .interval('5m') + .rate(1) + .generator((timestamp) => + log + .create() + .message('This is an info message') + .logLevel('info') + .timestamp(timestamp) + .defaults({ + 'log.file.path': '/my-service.log', + 'service.name': 'my-service', + 'host.name': hostName, + 'service.environment': 'production', + }) + ), + ]); + }); + after(async () => { + await logSynthtrace.clean(); + }); + + it('returns log rate timeseries', async () => { + const response = await getLogsRateTimeseries(); + expect(response.status).to.be(200); + expect( + response.body.currentPeriod[serviceName].every(({ y }) => y === 0.06666666666666667) + ).to.be(true); + }); + + it('handles environment filter', async () => { + const response = await getLogsRateTimeseries({ query: { environment: 'foo' } }); + expect(response.status).to.be(200); + expect(response.body.currentPeriod).to.empty(); + }); + + describe('when my-service is selected', () => { + it('returns some data', async () => { + const response = await getLogsRateTimeseries({ + path: { serviceName: 'my-service' }, + }); + + expect(response.status).to.be(200); + expect(first(response.body.currentPeriod?.['my-service'])?.y).to.be( + 0.18181818181818182 + ); + expect(last(response.body.currentPeriod?.['my-service'])?.y).to.be(0.09090909090909091); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index 3e490a621d3f..f8c035298447 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -15,6 +15,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); + loadTestFile(require.resolve('./entities')); loadTestFile(require.resolve('./cold_start')); }); } diff --git a/x-pack/test/apm_api_integration/tests/entities/logs/service_logs_rate_timeseries.spec.ts b/x-pack/test/apm_api_integration/tests/entities/logs/service_logs_rate_timeseries.spec.ts deleted file mode 100644 index d4717b25bba9..000000000000 --- a/x-pack/test/apm_api_integration/tests/entities/logs/service_logs_rate_timeseries.spec.ts +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { log, timerange } from '@kbn/apm-synthtrace-client'; -import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; -import { APIClientRequestParamsOf } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; -import { first, last } from 'lodash'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const logSynthtrace = getService('logSynthtraceEsClient'); - - const serviceName = 'synth-go'; - const start = new Date('2024-01-01T00:00:00.000Z').getTime(); - const end = new Date('2024-01-01T00:15:00.000Z').getTime() - 1; - - const hostName = 'synth-host'; - - async function getLogsRateTimeseries( - overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries'>['params'] - > - ) { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries', - params: { - path: { - serviceName: 'synth-go', - ...overrides?.path, - }, - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - environment: 'ENVIRONMENT_ALL', - kuery: '', - ...overrides?.query, - }, - }, - }); - return response; - } - - registry.when( - 'Logs rate timeseries when data is not loaded', - { config: 'basic', archives: [] }, - () => { - describe('Logs rate api', () => { - it('handles the empty state', async () => { - const response = await getLogsRateTimeseries(); - expect(response.status).to.be(200); - expect(response.body.currentPeriod).to.empty(); - }); - }); - } - ); - - registry.when('Logs rate timeseries when data loaded', { config: 'basic', archives: [] }, () => { - describe('Logs without log level field', () => { - before(async () => { - return logSynthtrace.index([ - timerange(start, end) - .interval('1m') - .rate(1) - .generator((timestamp) => - log.create().message('This is a log message').timestamp(timestamp).defaults({ - 'log.file.path': '/my-service.log', - 'service.name': serviceName, - 'host.name': hostName, - }) - ), - ]); - }); - after(async () => { - await logSynthtrace.clean(); - }); - - it('returns {} if log level is not available ', async () => { - const response = await getLogsRateTimeseries(); - expect(response.status).to.be(200); - }); - }); - - describe('Logs with log.level=error', () => { - before(async () => { - return logSynthtrace.index([ - timerange(start, end) - .interval('1m') - .rate(1) - .generator((timestamp) => - log - .create() - .message('This is a log message') - .logLevel('error') - .timestamp(timestamp) - .defaults({ - 'log.file.path': '/my-service.log', - 'service.name': serviceName, - 'host.name': hostName, - 'service.environment': 'test', - }) - ), - timerange(start, end) - .interval('2m') - .rate(1) - .generator((timestamp) => - log - .create() - .message('This is an error log message') - .logLevel('error') - .timestamp(timestamp) - .defaults({ - 'log.file.path': '/my-service.log', - 'service.name': 'my-service', - 'host.name': hostName, - 'service.environment': 'production', - }) - ), - timerange(start, end) - .interval('5m') - .rate(1) - .generator((timestamp) => - log - .create() - .message('This is an info message') - .logLevel('info') - .timestamp(timestamp) - .defaults({ - 'log.file.path': '/my-service.log', - 'service.name': 'my-service', - 'host.name': hostName, - 'service.environment': 'production', - }) - ), - ]); - }); - after(async () => { - await logSynthtrace.clean(); - }); - - it('returns log rate timeseries', async () => { - const response = await getLogsRateTimeseries(); - expect(response.status).to.be(200); - expect( - response.body.currentPeriod[serviceName].every(({ y }) => y === 0.06666666666666667) - ).to.be(true); - }); - - it('handles environment filter', async () => { - const response = await getLogsRateTimeseries({ query: { environment: 'foo' } }); - expect(response.status).to.be(200); - expect(response.body.currentPeriod).to.empty(); - }); - - describe('when my-service is selected', () => { - it('returns some data', async () => { - const response = await getLogsRateTimeseries({ - path: { serviceName: 'my-service' }, - }); - - expect(response.status).to.be(200); - expect(first(response.body.currentPeriod?.['my-service'])?.y).to.be(0.18181818181818182); - expect(last(response.body.currentPeriod?.['my-service'])?.y).to.be(0.09090909090909091); - }); - }); - }); - }); -} From 2277e648e51b08f653191f233381e992fc828ec4 Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:26:12 +0100 Subject: [PATCH 016/100] [ES|QL] Pretty-printing support for `GROK` and `DISSECT` commands (#199646) ## Summary Closes https://github.com/elastic/kibana/issues/199480 This fixes `GROK` and `DISSECT` command pretty-printing, in, both, `BasicPrettyPrinter` and `WrappingPrettyPrinting`. The two commands are special, because unlike all other commands they exhibit these two traits: 1. Command arguments are not separated by comma (in all other commands arguments are separated by comma). 2. Command option label is separated by `=` from the option value (in all other commands it is just a space). ``` DISSECT input "pattern" APPEND_SEPARATOR = "separator" | | | | no comma? | | equals sign? ``` ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) --- .../__tests__/basic_pretty_printer.test.ts | 33 +++++--- .../__tests__/wrapping_pretty_printer.test.ts | 77 +++++++++++++++++++ .../src/pretty_print/basic_pretty_printer.ts | 9 ++- .../src/pretty_print/constants.ts | 52 +++++++++++++ .../pretty_print/wrapping_pretty_printer.ts | 13 +++- 5 files changed, 169 insertions(+), 15 deletions(-) create mode 100644 packages/kbn-esql-ast/src/pretty_print/constants.ts diff --git a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts index 9e21c45f75b4..c8880b9bfe67 100644 --- a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts +++ b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts @@ -78,15 +78,6 @@ describe('single line query', () => { }); }); - describe('SHOW', () => { - /** @todo Enable once show command args are parsed as columns. */ - test.skip('info page', () => { - const { text } = reprint('SHOW info'); - - expect(text).toBe('SHOW info'); - }); - }); - describe('STATS', () => { test('with aggregates assignment', () => { const { text } = reprint('FROM a | STATS var = agg(123, fn(true))'); @@ -100,6 +91,30 @@ describe('single line query', () => { expect(text).toBe('FROM a | STATS A(1), B(2) BY asdf'); }); }); + + describe('GROK', () => { + test('two basic arguments', () => { + const { text } = reprint('FROM search-movies | GROK Awards "text"'); + + expect(text).toBe('FROM search-movies | GROK Awards "text"'); + }); + }); + + describe('DISSECT', () => { + test('two basic arguments', () => { + const { text } = reprint('FROM index | DISSECT input "pattern"'); + + expect(text).toBe('FROM index | DISSECT input "pattern"'); + }); + + test('with APPEND_SEPARATOR option', () => { + const { text } = reprint( + 'FROM index | DISSECT input "pattern" APPEND_SEPARATOR=""' + ); + + expect(text).toBe('FROM index | DISSECT input "pattern" APPEND_SEPARATOR = ""'); + }); + }); }); describe('expressions', () => { diff --git a/packages/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts b/packages/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts index 2dfe239ce5b8..6422ae9a451a 100644 --- a/packages/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts +++ b/packages/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts @@ -19,6 +19,83 @@ const reprint = (src: string, opts?: WrappingPrettyPrinterOptions) => { return { text }; }; +describe('commands', () => { + describe('GROK', () => { + test('two basic arguments', () => { + const { text } = reprint('FROM search-movies | GROK Awards "text"'); + + expect(text).toBe('FROM search-movies | GROK Awards "text"'); + }); + + test('two long arguments', () => { + const { text } = reprint( + 'FROM search-movies | GROK AwardsAwardsAwardsAwardsAwardsAwardsAwardsAwards "texttexttexttexttexttexttexttexttexttexttexttexttexttexttext"' + ); + + expect('\n' + text).toBe(` +FROM search-movies + | GROK + AwardsAwardsAwardsAwardsAwardsAwardsAwardsAwards + "texttexttexttexttexttexttexttexttexttexttexttexttexttexttext"`); + }); + }); + + describe('DISSECT', () => { + test('two basic arguments', () => { + const { text } = reprint('FROM index | DISSECT input "pattern"'); + + expect(text).toBe('FROM index | DISSECT input "pattern"'); + }); + + test('two long arguments', () => { + const { text } = reprint( + 'FROM index | DISSECT InputInputInputInputInputInputInputInputInputInputInputInputInputInput "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern"' + ); + + expect('\n' + text).toBe(` +FROM index + | DISSECT + InputInputInputInputInputInputInputInputInputInputInputInputInputInput + "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern"`); + }); + + test('with APPEND_SEPARATOR option', () => { + const { text } = reprint( + 'FROM index | DISSECT input "pattern" APPEND_SEPARATOR=""' + ); + + expect(text).toBe('FROM index | DISSECT input "pattern" APPEND_SEPARATOR = ""'); + }); + + test('two long arguments with short APPEND_SEPARATOR option', () => { + const { text } = reprint( + 'FROM index | DISSECT InputInputInputInputInputInputInputInputInputInputInputInputInputInput "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern" APPEND_SEPARATOR="sep"' + ); + + expect('\n' + text).toBe(` +FROM index + | DISSECT + InputInputInputInputInputInputInputInputInputInputInputInputInputInput + "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern" + APPEND_SEPARATOR = "sep"`); + }); + + test('two long arguments with long APPEND_SEPARATOR option', () => { + const { text } = reprint( + 'FROM index | DISSECT InputInputInputInputInputInputInputInputInputInputInputInputInputInput "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern" APPEND_SEPARATOR=""' + ); + + expect('\n' + text).toBe(` +FROM index + | DISSECT + InputInputInputInputInputInputInputInputInputInputInputInputInputInput + "PatternPatternPatternPatternPatternPatternPatternPatternPatternPattern" + APPEND_SEPARATOR = + ""`); + }); + }); +}); + describe('casing', () => { test('can chose command name casing', () => { const query = 'FROM index | WHERE a == 123'; diff --git a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts index 2f1e3439cd3a..cf252825c243 100644 --- a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts +++ b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts @@ -19,6 +19,7 @@ import { import { ESQLAstBaseItem, ESQLAstCommand, ESQLAstQueryExpression } from '../types'; import { ESQLAstExpressionNode, Visitor } from '../visitor'; import { resolveItem } from '../visitor/utils'; +import { commandOptionsWithEqualsSeparator, commandsWithNoCommaArgSeparator } from './constants'; import { LeafPrinter } from './leaf_printer'; export interface BasicPrettyPrinterOptions { @@ -378,7 +379,8 @@ export class BasicPrettyPrinter { args += (args ? ', ' : '') + arg; } - const argsFormatted = args ? ` ${args}` : ''; + const separator = commandOptionsWithEqualsSeparator.has(ctx.node.name) ? ' = ' : ' '; + const argsFormatted = args ? `${separator}${args}` : ''; const optionFormatted = `${option}${argsFormatted}`; return optionFormatted; @@ -392,7 +394,10 @@ export class BasicPrettyPrinter { let options = ''; for (const source of ctx.visitArguments()) { - args += (args ? ', ' : '') + source; + const needsSeparator = !!args; + const needsComma = !commandsWithNoCommaArgSeparator.has(ctx.node.name); + const separator = needsSeparator ? (needsComma ? ',' : '') + ' ' : ''; + args += separator + source; } for (const option of ctx.visitOptions()) { diff --git a/packages/kbn-esql-ast/src/pretty_print/constants.ts b/packages/kbn-esql-ast/src/pretty_print/constants.ts new file mode 100644 index 000000000000..01208af98d02 --- /dev/null +++ b/packages/kbn-esql-ast/src/pretty_print/constants.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +/** + * This set tracks commands that don't use commas to separate their + * arguments. + * + * Normally ES|QL command arguments are separated by commas. + * + * ``` + * COMMAND arg1, arg2, arg3 + * ``` + * + * But there are some commands (namely `grok` and `dissect`) which don't + * use commas to separate their arguments. + * + * ``` + * GROK input "pattern" + * DISSECT input "pattern" + * ``` + */ +export const commandsWithNoCommaArgSeparator = new Set(['grok', 'dissect']); + +/** + * This set tracks command options which use an equals sign to separate + * the option label from the option value. + * + * Most ES|QL commands use a space to separate the option label from the + * option value. + * + * ``` + * COMMAND arg1, arg2, arg3 OPTION option + * FROM index METADATA _id + * ``` + * + * However, the `APPEND_SEPARATOR` in the `DISSECT` command uses an equals + * sign to separate the option label from the option value. + * + * ``` + * DISSECT input "pattern" APPEND_SEPARATOR = "separator" + * | + * | + * equals sign + * ``` + */ +export const commandOptionsWithEqualsSeparator = new Set(['append_separator']); diff --git a/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts index 91f65a389f0c..2f863524740e 100644 --- a/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts +++ b/packages/kbn-esql-ast/src/pretty_print/wrapping_pretty_printer.ts @@ -20,6 +20,7 @@ import { } from '../visitor'; import { children, singleItems } from '../visitor/utils'; import { BasicPrettyPrinter, BasicPrettyPrinterOptions } from './basic_pretty_printer'; +import { commandOptionsWithEqualsSeparator, commandsWithNoCommaArgSeparator } from './constants'; import { getPrettyPrintStats } from './helpers'; import { LeafPrinter } from './leaf_printer'; @@ -259,6 +260,8 @@ export class WrappingPrettyPrinter { } } + const commaBetweenArgs = !commandsWithNoCommaArgSeparator.has(ctx.node.name); + if (!oneArgumentPerLine) { ARGS: for (const arg of singleItems(ctx.arguments())) { if (arg.type === 'option') { @@ -271,7 +274,8 @@ export class WrappingPrettyPrinter { if (formattedArgLength > largestArg) { largestArg = formattedArgLength; } - let separator = txt ? ',' : ''; + + let separator = txt ? (commaBetweenArgs ? ',' : '') : ''; let fragment = ''; if (needsWrap) { @@ -329,7 +333,7 @@ export class WrappingPrettyPrinter { const arg = ctx.visitExpression(args[i], { indent, remaining: this.opts.wrap - indent.length, - suffix: isLastArg ? '' : ',', + suffix: isLastArg ? '' : commaBetweenArgs ? ',' : '', }); const separator = isFirstArg ? '' : '\n'; const indentation = arg.indented ? '' : indent; @@ -557,8 +561,9 @@ export class WrappingPrettyPrinter { indent: inp.indent, remaining: inp.remaining - option.length - 1, }); - const argsFormatted = args.txt ? ` ${args.txt}` : ''; - const txt = `${option}${argsFormatted}`; + const argsFormatted = args.txt ? `${args.txt[0] === '\n' ? '' : ' '}${args.txt}` : ''; + const separator = commandOptionsWithEqualsSeparator.has(ctx.node.name) ? ' =' : ''; + const txt = `${option}${separator}${argsFormatted}`; return { txt, lines: args.lines }; }) From 68a5308bd5b4a4caa0cdaaa5d6b6d80553ceee9a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 12 Nov 2024 04:56:34 +1100 Subject: [PATCH 017/100] Authorized route migration for routes owned by security-defend-workflows (#198381) ### Authz API migration for authorized routes This PR migrates `access:` tags used in route definitions to new security configuration. --- .../endpoint/routes/actions/audit_log.ts | 7 +- .../server/endpoint/routes/actions/details.ts | 7 +- .../routes/actions/file_download_handler.ts | 7 +- .../routes/actions/file_info_handler.ts | 7 +- .../server/endpoint/routes/actions/list.ts | 7 +- .../routes/actions/response_actions.ts | 77 ++++++++++++++++--- .../server/endpoint/routes/actions/state.ts | 7 +- .../server/endpoint/routes/actions/status.ts | 7 +- .../routes/agent/agent_status_handler.ts | 7 +- .../server/endpoint/routes/metadata/index.ts | 21 ++++- .../endpoint/routes/metadata/metadata.test.ts | 4 +- .../routes/protection_updates_note/index.ts | 14 +++- .../endpoint/routes/suggestions/index.ts | 14 +++- 13 files changed, 158 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.ts index 196ffc71162d..8ba1de0bb70d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.ts @@ -27,7 +27,12 @@ export function registerActionAuditLogRoutes( .get({ access: 'public', path: ENDPOINT_ACTION_LOG_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts index 96b466a251cf..1d524b08aefc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/details.ts @@ -32,7 +32,12 @@ export const registerActionDetailsRoutes = ( .get({ access: 'public', path: ACTION_DETAILS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts index 2e16c57886f7..29aa6f4bba3d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_download_handler.ts @@ -38,7 +38,12 @@ export const registerActionFileDownloadRoutes = ( // we need to enable setting the version number via query params enableQueryVersion: true, path: ACTION_AGENT_FILE_DOWNLOAD_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts index 1cb4e95e1eaf..63118a64fc45 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/file_info_handler.ts @@ -74,7 +74,12 @@ export const registerActionFileInfoRoute = ( .get({ access: 'public', path: ACTION_AGENT_FILE_INFO_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.ts index a858909f5e2e..05e5d77eb945 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/list.ts @@ -30,7 +30,12 @@ export function registerActionListRoutes( .get({ access: 'public', path: BASE_ENDPOINT_ACTION_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts index b6eb2376bd1c..0fc90c7589b9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts @@ -80,7 +80,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: ISOLATE_HOST_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -99,7 +104,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: UNISOLATE_HOST_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -119,7 +129,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: ISOLATE_HOST_ROUTE_V2, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -139,7 +154,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: UNISOLATE_HOST_ROUTE_V2, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -159,7 +179,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: KILL_PROCESS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -182,7 +207,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: SUSPEND_PROCESS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -205,7 +235,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: GET_PROCESSES_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -225,7 +260,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: GET_FILE_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -245,7 +285,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: EXECUTE_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -265,9 +310,14 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: UPLOAD_ROUTE, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, options: { authRequired: true, - tags: ['access:securitySolution'], + body: { accepts: ['multipart/form-data'], output: 'stream', @@ -293,7 +343,12 @@ export function registerResponseActionRoutes( .post({ access: 'public', path: SCAN_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts index 3ea4d9fa3575..c2d5b850d6bb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/state.ts @@ -32,7 +32,12 @@ export function registerActionStateRoutes( .get({ access: 'public', path: ACTION_STATE_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts index 6172bf07d232..8a245dfb451e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts @@ -29,7 +29,12 @@ export function registerActionStatusRoutes( .get({ access: 'public', path: ACTION_STATUS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/agent/agent_status_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/agent/agent_status_handler.ts index e6ea2f759578..7ca24156b45f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/agent/agent_status_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/agent/agent_status_handler.ts @@ -27,7 +27,12 @@ export const registerAgentStatusRoute = ( .get({ access: 'internal', path: AGENT_STATUS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts index 2f6e46d1d772..3f028719fe5a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts @@ -54,7 +54,12 @@ export function registerEndpointRoutes( .get({ access: 'public', path: HOST_METADATA_LIST_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -94,7 +99,12 @@ export function registerEndpointRoutes( .get({ access: 'public', path: METADATA_TRANSFORMS_STATUS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) @@ -114,7 +124,12 @@ export function registerEndpointRoutes( .get({ access: 'internal', path: METADATA_TRANSFORMS_STATUS_INTERNAL_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 00054964e440..6010c5655727 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -240,8 +240,8 @@ describe('test endpoint routes', () => { }); expect(routeConfig.options).toEqual({ authRequired: true, - tags: ['access:securitySolution'], }); + expect(routeConfig.security?.authz).toEqual({ requiredPrivileges: ['securitySolution'] }); expect(mockResponse.ok).toBeCalled(); const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as MetadataListResponse; expect(endpointResultList.data.length).toEqual(1); @@ -614,8 +614,8 @@ describe('test endpoint routes', () => { expect(esClientMock.transform.getTransformStats).toHaveBeenCalledTimes(1); expect(routeConfig.options).toEqual({ authRequired: true, - tags: ['access:securitySolution'], }); + expect(routeConfig.security?.authz).toEqual({ requiredPrivileges: ['securitySolution'] }); expect(mockResponse.ok).toBeCalled(); const response = mockResponse.ok.mock.calls[0][0]?.body as TransformGetTransformStatsResponse; expect(response.count).toEqual(expectedResponse.count); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts index 7b28ccfcf9fe..4355684407bb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts @@ -25,7 +25,12 @@ export function registerProtectionUpdatesNoteRoutes( .post({ access: 'public', path: PROTECTION_UPDATES_NOTE_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { @@ -45,7 +50,12 @@ export function registerProtectionUpdatesNoteRoutes( .get({ access: 'public', path: PROTECTION_UPDATES_NOTE_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts index 677fb004ee86..bbee33114534 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts @@ -42,7 +42,12 @@ export function registerEndpointSuggestionsRoutes( .post({ access: 'public', path: SUGGESTIONS_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo} deprecated: true, }) @@ -64,7 +69,12 @@ export function registerEndpointSuggestionsRoutes( .post({ access: 'internal', path: SUGGESTIONS_INTERNAL_ROUTE, - options: { authRequired: true, tags: ['access:securitySolution'] }, + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, + options: { authRequired: true }, }) .addVersion( { From 10db64579ce7d3ac8d75724c2fbef11f10e2201e Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 11 Nov 2024 19:12:27 +0100 Subject: [PATCH 018/100] [ML] Anomaly Detection: Migrate chart tooltip from SCSS to emotion (#199417) ## Summary Part of https://github.com/elastic/kibana/issues/140695 Migrates SCSS to emotion for the Anomaly Detection chart tooltip. ![CleanShot 2024-11-08 at 08 01 22@2x](https://github.com/user-attachments/assets/a7cd94c2-15b7-42bc-b98a-3596eaeeafe1) ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) --- .../chart_tooltip/_chart_tooltip.scss | 47 -------------- .../components/chart_tooltip/_index.scss | 1 - .../chart_tooltip/chart_tooltip.tsx | 32 ++++++--- .../chart_tooltip/chart_tooltip_styles.ts | 65 +++++++++++++++++++ 4 files changed, 88 insertions(+), 57 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss delete mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/_index.scss create mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_styles.ts diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss b/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss deleted file mode 100644 index 9f9f16dff7e1..000000000000 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/_chart_tooltip.scss +++ /dev/null @@ -1,47 +0,0 @@ -.mlChartTooltip { - @include euiToolTipStyle('s'); - @include euiFontSizeXS; - padding: 0; - transition: opacity $euiAnimSpeedNormal; - pointer-events: none; - user-select: none; - max-width: 512px; - - &__list { - margin: $euiSizeXS; - padding-bottom: $euiSizeXS; - } - - &__header { - font-weight: $euiFontWeightBold; - padding: $euiSizeXS ($euiSizeXS * 2); - margin-bottom: $euiSizeXS; - border-bottom: $euiBorderThin solid transparentize($euiBorderColor, .8); - } - - &__item { - display: flex; - padding: 3px; - box-sizing: border-box; - border-left: $euiSizeXS solid transparent; - } - - &__label { - min-width: 1px; - } - - &__value { - font-weight: $euiFontWeightBold; - text-align: right; - font-feature-settings: 'tnum'; - margin-left: 8px; - } - - &__rowHighlighted { - background-color: transparentize($euiColorGhost, .9); - } - - &--hidden { - opacity: 0; - } -} diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/_index.scss b/x-pack/plugins/ml/public/application/components/chart_tooltip/_index.scss deleted file mode 100644 index 11b36a0a2100..000000000000 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'chart_tooltip'; \ No newline at end of file diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx index 0c6fe9095f4e..f279175d0110 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx @@ -9,14 +9,14 @@ import type { FC } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import TooltipTrigger from 'react-popper-tooltip'; +import type { ChildrenArg, TooltipTriggerProps } from 'react-popper-tooltip/dist/types'; + import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { TooltipValueFormatter } from '@elastic/charts'; -import './_index.scss'; - -import type { ChildrenArg, TooltipTriggerProps } from 'react-popper-tooltip/dist/types'; import type { ChartTooltipValue, TooltipData } from './chart_tooltip_service'; import { ChartTooltipService } from './chart_tooltip_service'; +import { useChartTooltipStyles } from './chart_tooltip_styles'; const renderHeader = (headerData?: ChartTooltipValue, formatter?: TooltipValueFormatter) => { if (!headerData) { @@ -30,17 +30,26 @@ const renderHeader = (headerData?: ChartTooltipValue, formatter?: TooltipValueFo * Pure component for rendering the tooltip content with a custom layout across the ML plugin. */ export const FormattedTooltip: FC<{ tooltipData: TooltipData }> = ({ tooltipData }) => { + const { + mlChartTooltip, + mlChartTooltipList, + mlChartTooltipHeader, + mlChartTooltipItem, + mlChartTooltipLabel, + mlChartTooltipValue, + } = useChartTooltipStyles(); + return ( -
+
{tooltipData.length > 0 && tooltipData[0].skipHeader === undefined && ( -
{renderHeader(tooltipData[0])}
+
{renderHeader(tooltipData[0])}
)} {tooltipData.length > 1 && ( -
+
{tooltipData .slice(1) .map(({ label, value, color, isHighlighted, seriesIdentifier, valueAccessor }) => { - const classes = classNames('mlChartTooltip__item', { + const classes = classNames({ // eslint-disable-next-line @typescript-eslint/naming-convention echTooltip__rowHighlighted: isHighlighted, }); @@ -52,16 +61,21 @@ export const FormattedTooltip: FC<{ tooltipData: TooltipData }> = ({ tooltipData return (
- + {label} - + {renderValue} diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_styles.ts b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_styles.ts new file mode 100644 index 000000000000..c53bdb5242f3 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_styles.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/react'; + +import { mathWithUnits, transparentize, useEuiTheme } from '@elastic/eui'; +// @ts-expect-error style types not defined +import { euiToolTipStyles } from '@elastic/eui/lib/components/tool_tip/tool_tip.styles'; + +import { useCurrentEuiThemeVars } from '@kbn/ml-kibana-theme'; + +import { useMlKibana } from '../../contexts/kibana'; + +export const useChartTooltipStyles = () => { + const euiThemeContext = useEuiTheme(); + const { + services: { theme }, + } = useMlKibana(); + const { euiTheme } = useCurrentEuiThemeVars(theme); + const euiStyles = euiToolTipStyles(euiThemeContext); + + return { + mlChartTooltip: css([ + euiStyles.euiToolTip, + { + fontSize: euiTheme.euiFontSizeXS, + padding: 0, + transition: `opacity ${euiTheme.euiAnimSpeedNormal}`, + pointerEvents: 'none', + userSelect: 'none', + maxWidth: '512px', + position: 'relative', + }, + ]), + mlChartTooltipList: css({ + margin: euiTheme.euiSizeXS, + paddingBottom: euiTheme.euiSizeXS, + }), + mlChartTooltipHeader: css({ + fontWeight: euiTheme.euiFontWeightBold, + padding: `${euiTheme.euiSizeXS} ${mathWithUnits(euiTheme.euiSizeS, (x) => x * 2)}`, + marginBottom: euiTheme.euiSizeXS, + borderBottom: `1px solid ${transparentize(euiTheme.euiBorderColor, 0.8)}`, + }), + mlChartTooltipItem: css({ + display: 'flex', + padding: '3px', + boxSizing: 'border-box', + borderLeft: `${euiTheme.euiSizeXS} solid transparent`, + }), + mlChartTooltipLabel: css({ + minWidth: '1px', + }), + mlChartTooltipValue: css({ + fontWeight: euiTheme.euiFontWeightBold, + textAlign: 'right', + fontFeatureSettings: 'tnum', + marginLeft: '8px', + }), + }; +}; From aeef51f2dd6858792e8c3d54c772b91ae20ebb9b Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 11 Nov 2024 19:12:51 +0100 Subject: [PATCH 019/100] [ML] Data Frame Analytics: Migrate scatterplot matrix from SCSS to emotion (#199180) ## Summary Part of https://github.com/elastic/kibana/issues/140695 Migrates SCSS to emotion for the DFA scatterplot matrix. ![CleanShot 2024-11-06 at 17 27 35@2x](https://github.com/user-attachments/assets/a022b4b8-a659-495d-ae82-10b65cf42579) ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) --- .../scatterplot_matrix/scatterplot_matrix.scss | 8 -------- .../scatterplot_matrix/scatterplot_matrix.tsx | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss deleted file mode 100644 index 322cdb4971f0..000000000000 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss +++ /dev/null @@ -1,8 +0,0 @@ -.mlScatterplotMatrix { - overflow-x: auto; - - .vega-bind span { - font-size: $euiFontSizeXS; - padding: 0 $euiSizeXS; - } -} \ No newline at end of file diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index dab7dc411708..763addd4aaa8 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -7,6 +7,7 @@ import type { FC } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { css } from '@emotion/react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; @@ -35,6 +36,7 @@ import { type RuntimeMappings, } from '@kbn/ml-runtime-field-utils'; import { getProcessedFields } from '@kbn/ml-data-grid'; +import { euiThemeVars } from '@kbn/ui-theme'; import { useCurrentThemeVars, useMlApi, useMlKibana } from '../../contexts/kibana'; @@ -48,7 +50,17 @@ import { OUTLIER_SCORE_FIELD, } from './scatterplot_matrix_vega_lite_spec'; -import './scatterplot_matrix.scss'; +const cssOverrides = css({ + // Prevent the chart from overflowing the container + overflowX: 'auto', + // Overrides for the outlier threshold slider + '.vega-bind': { + span: { + fontSize: euiThemeVars.euiFontSizeXS, + padding: `0 ${euiThemeVars.euiSizeXS}`, + }, + }, +}); const SCATTERPLOT_MATRIX_DEFAULT_FIELDS = 4; const SCATTERPLOT_MATRIX_DEFAULT_FETCH_SIZE = 1000; @@ -413,7 +425,7 @@ export const ScatterplotMatrix: FC = ({ ) : (
From 3b0c3808ef34db432c4e879a029e67188a01539b Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:08:27 +0000 Subject: [PATCH 020/100] [SecuritySolution] Generic reportEvents for EBT Telemetry (#197079) ## Summary 1. Removing the custom EBT events: https://github.com/elastic/kibana/pull/197079/files#diff-7beaf4f2d7c25c8913607b5dbc6e1ad0027f6ffacddafe4c675c775c3e7ae903L55 2. Use `reportEvent` for all the Security Solution EBT events. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../public/app/actions/telemetry.test.ts | 5 +- .../public/app/actions/telemetry.ts | 3 +- .../use_assistant_telemetry/index.test.tsx | 35 ++- .../use_assistant_telemetry/index.tsx | 41 +-- .../public/cases/pages/index.tsx | 3 +- .../control_columns/row_action/index.tsx | 7 +- .../public/common/components/links/index.tsx | 5 +- .../hooks/use_enable_data_feed.test.tsx | 14 +- .../ml_popover/hooks/use_enable_data_feed.ts | 13 +- .../breadcrumbs/use_breadcrumbs_nav.test.ts | 6 +- .../breadcrumbs/use_breadcrumbs_nav.ts | 8 +- .../public/common/lib/telemetry/constants.ts | 49 ---- .../telemetry/events/ai_assistant/index.ts | 27 +- .../telemetry/events/ai_assistant/types.ts | 42 ++- .../telemetry/events/alerts_grouping/index.ts | 22 +- .../telemetry/events/alerts_grouping/types.ts | 39 ++- .../common/lib/telemetry/events/app/index.ts | 58 ++++ .../common/lib/telemetry/events/app/types.ts | 34 +++ .../telemetry/events/data_quality/index.ts | 19 +- .../telemetry/events/data_quality/types.ts | 22 +- .../events/document_details/index.ts | 17 +- .../events/document_details/types.ts | 30 +-- .../events/entity_analytics/index.ts | 125 +++++++-- .../events/entity_analytics/types.ts | 138 +++++----- .../lib/telemetry/events/event_log/index.ts | 11 +- .../lib/telemetry/events/event_log/types.ts | 29 +- .../telemetry/events/manual_rule_run/index.ts | 22 +- .../telemetry/events/manual_rule_run/types.ts | 38 ++- .../lib/telemetry/events/notes/index.ts | 17 +- .../lib/telemetry/events/notes/types.ts | 30 +-- .../lib/telemetry/events/onboarding/index.ts | 22 +- .../lib/telemetry/events/onboarding/types.ts | 36 ++- .../telemetry/events/preview_rule/index.ts | 10 +- .../telemetry/events/preview_rule/types.ts | 15 +- .../lib/telemetry/events/telemetry_events.ts | 214 ++------------- .../public/common/lib/telemetry/index.ts | 14 - .../lib/telemetry/telemetry_client.mock.ts | 48 ---- .../common/lib/telemetry/telemetry_client.ts | 229 ---------------- .../lib/telemetry/telemetry_service.mock.ts | 4 +- .../lib/telemetry/telemetry_service.test.ts | 15 +- .../common/lib/telemetry/telemetry_service.ts | 21 +- .../public/common/lib/telemetry/types.ts | 250 +++++------------- .../rule_preview/use_preview_rule.ts | 3 +- .../execution_log_table.test.tsx | 4 +- .../execution_log_table.tsx | 3 +- .../stop_backfill.test.tsx | 16 +- .../rule_backfills_info/stop_backfill.tsx | 3 +- .../logic/use_schedule_rule_run.test.tsx | 33 ++- .../rule_gaps/logic/use_schedule_rule_run.ts | 5 +- .../bulk_actions/use_bulk_actions.tsx | 5 +- .../rules_table/use_rules_table_actions.tsx | 3 +- .../execution_run_type_filter/index.test.tsx | 14 +- .../execution_run_type_filter/index.tsx | 5 +- .../alerts_table/alerts_grouping.test.tsx | 23 +- .../alerts_table/alerts_grouping.tsx | 10 +- .../group_take_action_items.tsx | 16 +- .../rule_actions_overflow/index.test.tsx | 18 +- .../rules/rule_actions_overflow/index.tsx | 3 +- .../use_persistent_controls.tsx | 7 +- .../asset_criticality_file_uploader.tsx | 3 +- .../components/validation_step.test.tsx | 2 +- .../components/validation_step.tsx | 3 +- .../asset_criticality_file_uploader/hooks.ts | 5 +- .../anomalies_count_link.test.tsx | 6 +- .../anomalies_count_link.tsx | 3 +- .../entity_analytics_risk_score/index.tsx | 3 +- .../hooks/use_risk_input_actions.ts | 3 +- .../entity_store/hooks/use_entity_store.ts | 7 +- .../risk_summary_flyout/risk_summary.tsx | 3 +- .../severity/severity_filter.test.tsx | 6 +- .../components/severity/severity_filter.tsx | 3 +- .../left/components/host_details.tsx | 3 +- .../left/components/session_view.tsx | 3 +- .../left/components/user_details.tsx | 3 +- .../flyout/document_details/left/index.tsx | 3 +- .../left/tabs/insights_tab.tsx | 3 +- .../document_details/preview/footer.tsx | 3 +- .../right/components/alert_description.tsx | 3 +- .../right/components/reason.tsx | 3 +- .../flyout/document_details/right/index.tsx | 3 +- .../document_details/right/navigation.tsx | 3 +- .../shared/hooks/use_navigate_to_analyzer.tsx | 5 +- .../hooks/use_navigate_to_session_view.tsx | 5 +- .../entity_details/host_right/index.tsx | 3 +- .../entity_details/user_right/index.tsx | 3 +- .../flyout/shared/components/preview_link.tsx | 3 +- .../public/notes/components/add_note.tsx | 3 +- .../notes/components/open_flyout_button.tsx | 3 +- .../components/onboarding_context.tsx | 7 +- .../public/overview/pages/data_quality.tsx | 11 +- .../open_timeline/note_previews/index.tsx | 3 +- .../components/timeline/tabs/eql/index.tsx | 5 +- .../components/timeline/tabs/pinned/index.tsx | 5 +- .../components/timeline/tabs/query/index.tsx | 5 +- .../tabs/session/use_session_view.tsx | 3 +- .../unified_components/data_table/index.tsx | 3 +- .../plugins/security_solution/public/types.ts | 4 +- 97 files changed, 883 insertions(+), 1223 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/types.ts delete mode 100644 x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts delete mode 100644 x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts diff --git a/x-pack/plugins/security_solution/public/app/actions/telemetry.test.ts b/x-pack/plugins/security_solution/public/app/actions/telemetry.test.ts index 9b22aa03fbea..28d4b90f4d9e 100644 --- a/x-pack/plugins/security_solution/public/app/actions/telemetry.test.ts +++ b/x-pack/plugins/security_solution/public/app/actions/telemetry.test.ts @@ -9,6 +9,7 @@ import type { StartServices } from '../../types'; import { enhanceActionWithTelemetry } from './telemetry'; import { createAction } from '@kbn/ui-actions-plugin/public'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; +import { AppEventTypes } from '../../common/lib/telemetry'; const actionId = 'test_action_id'; const displayName = 'test-actions'; @@ -29,13 +30,13 @@ const context = { describe('enhanceActionWithTelemetry', () => { it('calls telemetry report when the action is executed', () => { - const telemetry = { reportCellActionClicked: jest.fn() }; + const telemetry = { reportEvent: jest.fn() }; const services = { telemetry } as unknown as StartServices; const enhancedAction = enhanceActionWithTelemetry(action, services); enhancedAction.execute(context); - expect(telemetry.reportCellActionClicked).toHaveBeenCalledWith({ + expect(telemetry.reportEvent).toHaveBeenCalledWith(AppEventTypes.CellActionClicked, { displayName, actionId, fieldName, diff --git a/x-pack/plugins/security_solution/public/app/actions/telemetry.ts b/x-pack/plugins/security_solution/public/app/actions/telemetry.ts index 319194e57f81..0c69f7b23366 100644 --- a/x-pack/plugins/security_solution/public/app/actions/telemetry.ts +++ b/x-pack/plugins/security_solution/public/app/actions/telemetry.ts @@ -9,6 +9,7 @@ import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions'; import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import type { StartServices } from '../../types'; import type { SecurityCellActionExecutionContext } from './types'; +import { AppEventTypes } from '../../common/lib/telemetry'; export const enhanceActionWithTelemetry = ( action: CellAction, @@ -19,7 +20,7 @@ export const enhanceActionWithTelemetry = ( const enhancedExecute = ( context: ActionExecutionContext ): Promise => { - telemetry.reportCellActionClicked({ + telemetry.reportEvent(AppEventTypes.CellActionClicked, { actionId: rest.id, displayName: rest.getDisplayName(context), fieldName: context.data.map(({ field }) => field.name).join(', '), diff --git a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx index d714781ee11c..b90db333742a 100644 --- a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx +++ b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.test.tsx @@ -9,6 +9,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useAssistantTelemetry } from '.'; import { BASE_SECURITY_CONVERSATIONS } from '../content/conversations'; import { createTelemetryServiceMock } from '../../common/lib/telemetry/telemetry_service.mock'; +import { AssistantEventTypes } from '../../common/lib/telemetry'; const customId = `My Convo`; const mockedConversations = { @@ -20,15 +21,9 @@ const mockedConversations = { messages: [], }, }; -const reportAssistantInvoked = jest.fn(); -const reportAssistantMessageSent = jest.fn(); -const reportAssistantQuickPrompt = jest.fn(); + const mockedTelemetry = { ...createTelemetryServiceMock(), - reportAssistantInvoked, - reportAssistantMessageSent, - reportAssistantQuickPrompt, - reportAssistantSettingToggled: () => {}, }; jest.mock('../../common/lib/kibana', () => { @@ -55,9 +50,9 @@ jest.mock('@kbn/elastic-assistant', () => ({ })); const trackingFns = [ - 'reportAssistantInvoked', - 'reportAssistantMessageSent', - 'reportAssistantQuickPrompt', + { name: 'reportAssistantInvoked', eventType: AssistantEventTypes.AssistantInvoked }, + { name: 'reportAssistantMessageSent', eventType: AssistantEventTypes.AssistantMessageSent }, + { name: 'reportAssistantQuickPrompt', eventType: AssistantEventTypes.AssistantQuickPrompt }, ]; describe('useAssistantTelemetry', () => { @@ -67,7 +62,7 @@ describe('useAssistantTelemetry', () => { it('should return the expected telemetry object with tracking functions', () => { const { result } = renderHook(() => useAssistantTelemetry()); trackingFns.forEach((fn) => { - expect(result.current).toHaveProperty(fn); + expect(result.current).toHaveProperty(fn.name); }); }); @@ -76,11 +71,11 @@ describe('useAssistantTelemetry', () => { const { result } = renderHook(() => useAssistantTelemetry()); const validId = Object.keys(mockedConversations)[0]; // @ts-ignore - const trackingFn = result.current[fn]; + const trackingFn = result.current[fn.name]; await trackingFn({ conversationId: validId, invokedBy: 'shortcut' }); // @ts-ignore - const trackingMockedFn = mockedTelemetry[fn]; - expect(trackingMockedFn).toHaveBeenCalledWith({ + const trackingMockedFn = mockedTelemetry.reportEvent; + expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, { conversationId: validId, invokedBy: 'shortcut', }); @@ -89,11 +84,11 @@ describe('useAssistantTelemetry', () => { it('Should call tracking with "Custom" id when tracking is called with an isDefault=false conversation id', async () => { const { result } = renderHook(() => useAssistantTelemetry()); // @ts-ignore - const trackingFn = result.current[fn]; + const trackingFn = result.current[fn.name]; await trackingFn({ conversationId: customId, invokedBy: 'shortcut' }); // @ts-ignore - const trackingMockedFn = mockedTelemetry[fn]; - expect(trackingMockedFn).toHaveBeenCalledWith({ + const trackingMockedFn = mockedTelemetry.reportEvent; + expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, { conversationId: 'Custom', invokedBy: 'shortcut', }); @@ -102,11 +97,11 @@ describe('useAssistantTelemetry', () => { it('Should call tracking with "Custom" id when tracking is called with an unknown conversation id', async () => { const { result } = renderHook(() => useAssistantTelemetry()); // @ts-ignore - const trackingFn = result.current[fn]; + const trackingFn = result.current[fn.name]; await trackingFn({ conversationId: '123', invokedBy: 'shortcut' }); // @ts-ignore - const trackingMockedFn = mockedTelemetry[fn]; - expect(trackingMockedFn).toHaveBeenCalledWith({ + const trackingMockedFn = mockedTelemetry.reportEvent; + expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, { conversationId: 'Custom', invokedBy: 'shortcut', }); diff --git a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.tsx b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.tsx index 543eac554beb..04bfc8bdcd64 100644 --- a/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/use_assistant_telemetry/index.tsx @@ -9,7 +9,13 @@ import { type AssistantTelemetry } from '@kbn/elastic-assistant'; import { useCallback } from 'react'; import { useKibana } from '../../common/lib/kibana'; import { useBaseConversations } from '../use_conversation_store'; - +import type { + ReportAssistantInvokedParams, + ReportAssistantMessageSentParams, + ReportAssistantQuickPromptParams, + ReportAssistantSettingToggledParams, +} from '../../common/lib/telemetry'; +import { AssistantEventTypes } from '../../common/lib/telemetry'; export const useAssistantTelemetry = (): AssistantTelemetry => { const { services: { telemetry }, @@ -27,27 +33,30 @@ export const useAssistantTelemetry = (): AssistantTelemetry => { const reportTelemetry = useCallback( async ({ - fn, + eventType, params: { conversationId, ...rest }, - }: // eslint-disable-next-line @typescript-eslint/no-explicit-any - any): Promise<{ - fn: keyof AssistantTelemetry; - params: AssistantTelemetry[keyof AssistantTelemetry]; - }> => - fn({ + }: { + eventType: AssistantEventTypes; + params: + | ReportAssistantInvokedParams + | ReportAssistantMessageSentParams + | ReportAssistantQuickPromptParams; + }) => + telemetry.reportEvent(eventType, { ...rest, conversationId: await getAnonymizedConversationTitle(conversationId), }), - [getAnonymizedConversationTitle] + [getAnonymizedConversationTitle, telemetry] ); return { - reportAssistantInvoked: (params) => - reportTelemetry({ fn: telemetry.reportAssistantInvoked, params }), - reportAssistantMessageSent: (params) => - reportTelemetry({ fn: telemetry.reportAssistantMessageSent, params }), - reportAssistantQuickPrompt: (params) => - reportTelemetry({ fn: telemetry.reportAssistantQuickPrompt, params }), - reportAssistantSettingToggled: (params) => telemetry.reportAssistantSettingToggled(params), + reportAssistantInvoked: (params: ReportAssistantInvokedParams) => + reportTelemetry({ eventType: AssistantEventTypes.AssistantInvoked, params }), + reportAssistantMessageSent: (params: ReportAssistantMessageSentParams) => + reportTelemetry({ eventType: AssistantEventTypes.AssistantMessageSent, params }), + reportAssistantQuickPrompt: (params: ReportAssistantQuickPromptParams) => + reportTelemetry({ eventType: AssistantEventTypes.AssistantQuickPrompt, params }), + reportAssistantSettingToggled: (params: ReportAssistantSettingToggledParams) => + telemetry.reportEvent(AssistantEventTypes.AssistantSettingToggled, params), }; }; diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index 77fc7db0c0a8..787dfc973c5d 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -25,6 +25,7 @@ import * as timelineMarkdownPlugin from '../../common/components/markdown_editor import { useFetchAlertData } from './use_fetch_alert_data'; import { useUpsellingMessage } from '../../common/hooks/use_upselling'; import { useFetchNotes } from '../../notes/hooks/use_fetch_notes'; +import { DocumentEventTypes } from '../../common/lib/telemetry'; const CaseContainerComponent: React.FC = () => { const { cases, telemetry } = useKibana().services; @@ -47,7 +48,7 @@ const CaseContainerComponent: React.FC = () => { }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: TimelineId.casePage, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx index aebf34f09402..a56010182f13 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx @@ -24,6 +24,7 @@ import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/t import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; import { useTourContext } from '../../guided_onboarding_tour'; import { AlertsCasesTourSteps, SecurityStepId } from '../../guided_onboarding_tour/tour_config'; +import { NotesEventTypes, DocumentEventTypes } from '../../../lib/telemetry'; import { getMappedNonEcsValue } from '../../../utils/get_mapped_non_ecs_value'; export type RowActionProps = EuiDataGridCellValueElementProps & { @@ -109,7 +110,7 @@ const RowActionComponent = ({ }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: tableId, panel: 'right', }); @@ -137,10 +138,10 @@ const RowActionComponent = ({ }, }, }); - telemetry.reportOpenNoteInExpandableFlyoutClicked({ + telemetry.reportEvent(NotesEventTypes.OpenNoteInExpandableFlyoutClicked, { location: tableId, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: tableId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index 0648dd60d84f..83042c2f4fbf 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -41,6 +41,7 @@ import { import type { HostsTableType } from '../../../explore/hosts/store/model'; import type { UsersTableType } from '../../../explore/users/store/model'; import { useGetSecuritySolutionLinkProps, withSecuritySolutionLink } from './link_props'; +import { EntityEventTypes } from '../../lib/telemetry'; export { useSecuritySolutionLinkProps, type GetSecuritySolutionLinkProps } from './link_props'; export { LinkButton, LinkAnchor } from './helpers'; @@ -94,7 +95,7 @@ const UserDetailsLinkComponent: React.FC<{ const onClick = useCallback( (e: SyntheticEvent) => { - telemetry.reportEntityDetailsClicked({ entity: 'user' }); + telemetry.reportEvent(EntityEventTypes.EntityDetailsClicked, { entity: 'user' }); const callback = onClickParam ?? goToUsersDetails; callback(e); }, @@ -171,7 +172,7 @@ const HostDetailsLinkComponent: React.FC = ({ const onClick = useCallback( (e: SyntheticEvent) => { - telemetry.reportEntityDetailsClicked({ entity: 'host' }); + telemetry.reportEvent(EntityEventTypes.EntityDetailsClicked, { entity: 'host' }); const callback = onClickParam ?? goToHostDetails; callback(e); diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx index 0801ec37f6ae..5d1d2ab2eaba 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.test.tsx @@ -11,7 +11,7 @@ import { TestProviders } from '../../../mock'; import type { SecurityJob } from '../types'; import { createTelemetryServiceMock } from '../../../lib/telemetry/telemetry_service.mock'; -import { ML_JOB_TELEMETRY_STATUS } from '../../../lib/telemetry'; +import { ML_JOB_TELEMETRY_STATUS, EntityEventTypes } from '../../../lib/telemetry'; const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} @@ -188,14 +188,14 @@ describe('useSecurityJobsHelpers', () => { await result.current.enableDatafeed(JOB, TIMESTAMP); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.moduleInstalled, isElasticJob: true, jobId, moduleId, }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.started, isElasticJob: true, jobId, @@ -211,7 +211,7 @@ describe('useSecurityJobsHelpers', () => { await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.startError, errorMessage: 'Start job failure - test_error', isElasticJob: true, @@ -228,7 +228,7 @@ describe('useSecurityJobsHelpers', () => { await result.current.enableDatafeed(JOB, TIMESTAMP); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.installationError, errorMessage: 'Create job failure - test_error', isElasticJob: true, @@ -295,7 +295,7 @@ describe('useSecurityJobsHelpers', () => { await result.current.disableDatafeed({ ...JOB, isInstalled: true }); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.stopped, isElasticJob: true, jobId, @@ -311,7 +311,7 @@ describe('useSecurityJobsHelpers', () => { await result.current.disableDatafeed({ ...JOB, isInstalled: true }); }); - expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({ + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, { status: ML_JOB_TELEMETRY_STATUS.stopError, errorMessage: 'Stop job failure - test_error', isElasticJob: true, diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts index 393e132436c3..ab966770aaf3 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/hooks/use_enable_data_feed.ts @@ -13,6 +13,7 @@ import { METRIC_TYPE, ML_JOB_TELEMETRY_STATUS, TELEMETRY_EVENT, + EntityEventTypes, track, } from '../../../lib/telemetry'; @@ -43,7 +44,7 @@ export const useEnableDataFeed = () => { jobIdErrorFilter: [job.id], groups: job.groups, }); - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, moduleId: job.moduleId, @@ -52,7 +53,7 @@ export const useEnableDataFeed = () => { } catch (error) { setIsLoading(false); addError(error, { title: i18n.CREATE_JOB_FAILURE }); - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, moduleId: job.moduleId, @@ -82,7 +83,7 @@ export const useEnableDataFeed = () => { throw new Error(response[datafeedId].error); } - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, status: ML_JOB_TELEMETRY_STATUS.started, @@ -92,7 +93,7 @@ export const useEnableDataFeed = () => { } catch (error) { track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE); addError(error, { title: i18n.START_JOB_FAILURE }); - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, status: ML_JOB_TELEMETRY_STATUS.startError, @@ -124,7 +125,7 @@ export const useEnableDataFeed = () => { throw new Error(response.error); } - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, status: ML_JOB_TELEMETRY_STATUS.stopped, @@ -134,7 +135,7 @@ export const useEnableDataFeed = () => { } catch (error) { track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE); addError(error, { title: i18n.STOP_JOB_FAILURE }); - telemetry.reportMLJobUpdate({ + telemetry.reportEvent(EntityEventTypes.MLJobUpdate, { jobId: job.id, isElasticJob: job.isElasticJob, status: ML_JOB_TELEMETRY_STATUS.stopError, diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts index c8c675f0f40a..c20d1f4623fa 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts @@ -137,12 +137,12 @@ describe('useBreadcrumbsNav', () => { }); it('should create breadcrumbs onClick handler', () => { - const reportBreadcrumbClickedMock = jest.fn(); + const reportEventMock = jest.fn(); (kibanaLib.useKibana as jest.Mock).mockImplementation(() => ({ services: { telemetry: { - reportBreadcrumbClicked: reportBreadcrumbClickedMock, + reportEvent: reportEventMock, }, }, })); @@ -157,6 +157,6 @@ describe('useBreadcrumbsNav', () => { expect(event.preventDefault).toHaveBeenCalled(); expect(mockDispatch).toHaveBeenCalled(); - expect(reportBreadcrumbClickedMock).toHaveBeenCalled(); + expect(reportEventMock).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts index 7825435fafca..aae96ddd07dc 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts @@ -16,7 +16,7 @@ import { timelineActions } from '../../../../timelines/store'; import { TimelineId } from '../../../../../common/types/timeline'; import type { GetSecuritySolutionUrl } from '../../link_to'; import { useGetSecuritySolutionUrl } from '../../link_to'; -import type { TelemetryClientStart } from '../../../lib/telemetry'; +import { AppEventTypes, type TelemetryServiceStart } from '../../../lib/telemetry'; import { useKibana, useNavigateTo, type NavigateTo } from '../../../lib/kibana'; import { useRouteSpy } from '../../../utils/route/use_route_spy'; import { updateBreadcrumbsNav } from '../../../breadcrumbs'; @@ -68,7 +68,7 @@ const addOnClicksHandlers = ( breadcrumbs: ChromeBreadcrumb[], dispatch: Dispatch, navigateTo: NavigateTo, - telemetry: TelemetryClientStart + telemetry: TelemetryServiceStart ): ChromeBreadcrumb[] => breadcrumbs.map((breadcrumb) => ({ ...breadcrumb, @@ -89,13 +89,13 @@ const createOnClickHandler = href: string, dispatch: Dispatch, navigateTo: NavigateTo, - telemetry: TelemetryClientStart, + telemetry: TelemetryServiceStart, title: React.ReactNode ) => (ev: SyntheticEvent) => { ev.preventDefault(); if (typeof title === 'string') { - telemetry.reportBreadcrumbClicked({ title }); + telemetry.reportEvent(AppEventTypes.BreadcrumbClicked, { title }); } dispatch(timelineActions.showTimeline({ id: TimelineId.active, show: false })); navigateTo({ url: href }); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts index 5d0e9bcfd918..08bc1d4a62a8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/constants.ts @@ -52,52 +52,3 @@ export enum TELEMETRY_EVENT { // AI assistant on rule creation form OPEN_ASSISTANT_ON_RULE_QUERY_ERROR = 'open_assistant_on_rule_query_error', } - -export enum TelemetryEventTypes { - AlertsGroupingChanged = 'Alerts Grouping Changed', - AlertsGroupingToggled = 'Alerts Grouping Toggled', - AlertsGroupingTakeAction = 'Alerts Grouping Take Action', - BreadcrumbClicked = 'Breadcrumb Clicked', - AssistantInvoked = 'Assistant Invoked', - AssistantMessageSent = 'Assistant Message Sent', - AssistantQuickPrompt = 'Assistant Quick Prompt', - AssistantSettingToggled = 'Assistant Setting Toggled', - AssetCriticalityCsvPreviewGenerated = 'Asset Criticality Csv Preview Generated', - AssetCriticalityFileSelected = 'Asset Criticality File Selected', - AssetCriticalityCsvImported = 'Asset Criticality CSV Imported', - EntityDetailsClicked = 'Entity Details Clicked', - EntityAlertsClicked = 'Entity Alerts Clicked', - EntityRiskFiltered = 'Entity Risk Filtered', - EntityStoreEnablementToggleClicked = 'Entity Store Enablement Toggle Clicked', - EntityStoreDashboardInitButtonClicked = 'Entity Store Initialization Button Clicked', - MLJobUpdate = 'ML Job Update', - AddRiskInputToTimelineClicked = 'Add Risk Input To Timeline Clicked', - ToggleRiskSummaryClicked = 'Toggle Risk Summary Clicked', - RiskInputsExpandedFlyoutOpened = 'Risk Inputs Expanded Flyout Opened', - CellActionClicked = 'Cell Action Clicked', - AnomaliesCountClicked = 'Anomalies Count Clicked', - DataQualityIndexChecked = 'Data Quality Index Checked', - DataQualityCheckAllCompleted = 'Data Quality Check All Completed', - DetailsFlyoutOpened = 'Details Flyout Opened', - DetailsFlyoutTabClicked = 'Details Flyout Tabs Clicked', - OnboardingHubStepOpen = 'Onboarding Hub Step Open', - OnboardingHubStepFinished = 'Onboarding Hub Step Finished', - OnboardingHubStepLinkClicked = 'Onboarding Hub Step Link Clicked', - ManualRuleRunOpenModal = 'Manual Rule Run Open Modal', - ManualRuleRunExecute = 'Manual Rule Run Execute', - ManualRuleRunCancelJob = 'Manual Rule Run Cancel Job', - EventLogFilterByRunType = 'Event Log Filter By Run Type', - EventLogShowSourceEventDateRange = 'Event Log -> Show Source -> Event Date Range', - OpenNoteInExpandableFlyoutClicked = 'Open Note In Expandable Flyout Clicked', - AddNoteFromExpandableFlyoutClicked = 'Add Note From Expandable Flyout Clicked', - PreviewRule = 'Preview rule', -} - -export enum ML_JOB_TELEMETRY_STATUS { - started = 'started', - startError = 'start_error', - stopped = 'stopped', - stopError = 'stop_error', - moduleInstalled = 'module_installed', - installationError = 'installationError', -} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/index.ts index 117d6216ed2a..70d2eb82a2c9 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { AssistantTelemetryEvent } from './types'; +import { AssistantEventTypes } from './types'; -export const assistantInvokedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssistantInvoked, +export const assistantInvokedEvent: AssistantTelemetryEvent = { + eventType: AssistantEventTypes.AssistantInvoked, schema: { conversationId: { type: 'keyword', @@ -28,8 +28,8 @@ export const assistantInvokedEvent: TelemetryEvent = { }, }; -export const assistantMessageSentEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssistantMessageSent, +export const assistantMessageSentEvent: AssistantTelemetryEvent = { + eventType: AssistantEventTypes.AssistantMessageSent, schema: { conversationId: { type: 'keyword', @@ -75,8 +75,8 @@ export const assistantMessageSentEvent: TelemetryEvent = { }, }; -export const assistantQuickPrompt: TelemetryEvent = { - eventType: TelemetryEventTypes.AssistantQuickPrompt, +export const assistantQuickPrompt: AssistantTelemetryEvent = { + eventType: AssistantEventTypes.AssistantQuickPrompt, schema: { conversationId: { type: 'keyword', @@ -95,8 +95,8 @@ export const assistantQuickPrompt: TelemetryEvent = { }, }; -export const assistantSettingToggledEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssistantSettingToggled, +export const assistantSettingToggledEvent: AssistantTelemetryEvent = { + eventType: AssistantEventTypes.AssistantSettingToggled, schema: { alertsCountUpdated: { type: 'boolean', @@ -114,3 +114,10 @@ export const assistantSettingToggledEvent: TelemetryEvent = { }, }, }; + +export const assistantTelemetryEvents = [ + assistantInvokedEvent, + assistantMessageSentEvent, + assistantQuickPrompt, + assistantSettingToggledEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/types.ts index 2dd6bf6215db..894494575f9a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/ai_assistant/types.ts @@ -6,7 +6,13 @@ */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; + +export enum AssistantEventTypes { + AssistantInvoked = 'Assistant Invoked', + AssistantMessageSent = 'Assistant Message Sent', + AssistantQuickPrompt = 'Assistant Quick Prompt', + AssistantSettingToggled = 'Assistant Setting Toggled', +} export interface ReportAssistantInvokedParams { conversationId: string; @@ -32,26 +38,14 @@ export interface ReportAssistantSettingToggledParams { assistantStreamingEnabled?: boolean; } -export type ReportAssistantTelemetryEventParams = - | ReportAssistantInvokedParams - | ReportAssistantMessageSentParams - | ReportAssistantSettingToggledParams - | ReportAssistantQuickPromptParams; - -export type AssistantTelemetryEvent = - | { - eventType: TelemetryEventTypes.AssistantInvoked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssistantSettingToggled; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssistantMessageSent; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssistantQuickPrompt; - schema: RootSchema; - }; +export interface AssistantTelemetryEventsMap { + [AssistantEventTypes.AssistantInvoked]: ReportAssistantInvokedParams; + [AssistantEventTypes.AssistantMessageSent]: ReportAssistantMessageSentParams; + [AssistantEventTypes.AssistantQuickPrompt]: ReportAssistantQuickPromptParams; + [AssistantEventTypes.AssistantSettingToggled]: ReportAssistantSettingToggledParams; +} + +export interface AssistantTelemetryEvent { + eventType: AssistantEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/index.ts index 7c990dc75776..3e2119205b77 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { AlertsGroupingTelemetryEvent } from './types'; +import { AlertsEventTypes } from './types'; -export const alertsGroupingToggledEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AlertsGroupingToggled, +export const alertsGroupingToggledEvent: AlertsGroupingTelemetryEvent = { + eventType: AlertsEventTypes.AlertsGroupingToggled, schema: { isOpen: { type: 'boolean', @@ -35,8 +35,8 @@ export const alertsGroupingToggledEvent: TelemetryEvent = { }, }; -export const alertsGroupingChangedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AlertsGroupingChanged, +export const alertsGroupingChangedEvent: AlertsGroupingTelemetryEvent = { + eventType: AlertsEventTypes.AlertsGroupingChanged, schema: { tableId: { type: 'keyword', @@ -55,8 +55,8 @@ export const alertsGroupingChangedEvent: TelemetryEvent = { }, }; -export const alertsGroupingTakeActionEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AlertsGroupingTakeAction, +export const alertsGroupingTakeActionEvent: AlertsGroupingTelemetryEvent = { + eventType: AlertsEventTypes.AlertsGroupingTakeAction, schema: { tableId: { type: 'keyword', @@ -88,3 +88,9 @@ export const alertsGroupingTakeActionEvent: TelemetryEvent = { }, }, }; + +export const alertsTelemetryEvents = [ + alertsGroupingToggledEvent, + alertsGroupingChangedEvent, + alertsGroupingTakeActionEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/types.ts index d2b5e227ee66..924ddd4d1987 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/alerts_grouping/types.ts @@ -6,41 +6,38 @@ */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface ReportAlertsGroupingChangedParams { +export enum AlertsEventTypes { + AlertsGroupingChanged = 'Alerts Grouping Changed', + AlertsGroupingToggled = 'Alerts Grouping Toggled', + AlertsGroupingTakeAction = 'Alerts Grouping Take Action', +} + +interface ReportAlertsGroupingChangedParams { tableId: string; groupByField: string; } -export interface ReportAlertsGroupingToggledParams { +interface ReportAlertsGroupingToggledParams { isOpen: boolean; tableId: string; groupNumber: number; } -export interface ReportAlertsTakeActionParams { +interface ReportAlertsTakeActionParams { tableId: string; groupNumber: number; status: 'open' | 'closed' | 'acknowledged'; groupByField: string; } -export type ReportAlertsGroupingTelemetryEventParams = - | ReportAlertsGroupingChangedParams - | ReportAlertsGroupingToggledParams - | ReportAlertsTakeActionParams; +export interface AlertsGroupingTelemetryEventsMap { + [AlertsEventTypes.AlertsGroupingChanged]: ReportAlertsGroupingChangedParams; + [AlertsEventTypes.AlertsGroupingToggled]: ReportAlertsGroupingToggledParams; + [AlertsEventTypes.AlertsGroupingTakeAction]: ReportAlertsTakeActionParams; +} -export type AlertsGroupingTelemetryEvent = - | { - eventType: TelemetryEventTypes.AlertsGroupingToggled; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AlertsGroupingChanged; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AlertsGroupingTakeAction; - schema: RootSchema; - }; +export interface AlertsGroupingTelemetryEvent { + eventType: AlertsEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/index.ts new file mode 100644 index 000000000000..d00d1df5f830 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/index.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AppTelemetryEvent } from './types'; +import { AppEventTypes } from './types'; + +const cellActionClickedEvent: AppTelemetryEvent = { + eventType: AppEventTypes.CellActionClicked, + schema: { + fieldName: { + type: 'keyword', + _meta: { + description: 'Field Name', + optional: false, + }, + }, + actionId: { + type: 'keyword', + _meta: { + description: 'Action id', + optional: false, + }, + }, + displayName: { + type: 'keyword', + _meta: { + description: 'User friendly action name', + optional: false, + }, + }, + metadata: { + type: 'pass_through', + _meta: { + description: 'Action metadata', + optional: true, + }, + }, + }, +}; + +const breadCrumbClickedEvent: AppTelemetryEvent = { + eventType: AppEventTypes.BreadcrumbClicked, + schema: { + title: { + type: 'keyword', + _meta: { + description: 'Breadcrumb title', + optional: false, + }, + }, + }, +}; + +export const appTelemetryEvents = [cellActionClickedEvent, breadCrumbClickedEvent]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/types.ts new file mode 100644 index 000000000000..f42e689cc3fd --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/app/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { RootSchema } from '@kbn/core/public'; +import type { SecurityCellActionMetadata } from '../../../../../app/actions/types'; + +export enum AppEventTypes { + CellActionClicked = 'Cell Action Clicked', + BreadcrumbClicked = 'Breadcrumb Clicked', +} + +interface ReportCellActionClickedParams { + metadata: SecurityCellActionMetadata | undefined; + displayName: string; + actionId: string; + fieldName: string; +} + +interface ReportBreadcrumbClickedParams { + title: string; +} + +export interface AppTelemetryEventsMap { + [AppEventTypes.CellActionClicked]: ReportCellActionClickedParams; + [AppEventTypes.BreadcrumbClicked]: ReportBreadcrumbClickedParams; +} + +export interface AppTelemetryEvent { + eventType: AppEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/index.ts index 1a3a88cbd2f5..16e8a3e1eaa6 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/index.ts @@ -5,14 +5,10 @@ * 2.0. */ -import { TelemetryEventTypes } from '../../constants'; -import type { - DataQualityTelemetryCheckAllCompletedEvent, - DataQualityTelemetryIndexCheckedEvent, -} from '../../types'; +import { DataQualityEventTypes, type DataQualityTelemetryEvents } from './types'; -export const dataQualityIndexCheckedEvent: DataQualityTelemetryIndexCheckedEvent = { - eventType: TelemetryEventTypes.DataQualityIndexChecked, +export const dataQualityIndexCheckedEvent: DataQualityTelemetryEvents = { + eventType: DataQualityEventTypes.DataQualityIndexChecked, schema: { batchId: { type: 'keyword', @@ -163,8 +159,8 @@ export const dataQualityIndexCheckedEvent: DataQualityTelemetryIndexCheckedEvent }, }; -export const dataQualityCheckAllClickedEvent: DataQualityTelemetryCheckAllCompletedEvent = { - eventType: TelemetryEventTypes.DataQualityCheckAllCompleted, +export const dataQualityCheckAllClickedEvent: DataQualityTelemetryEvents = { + eventType: DataQualityEventTypes.DataQualityCheckAllCompleted, schema: { batchId: { type: 'keyword', @@ -259,3 +255,8 @@ export const dataQualityCheckAllClickedEvent: DataQualityTelemetryCheckAllComple }, }, }; + +export const dataQualityTelemetryEvents = [ + dataQualityIndexCheckedEvent, + dataQualityCheckAllClickedEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/types.ts index 9e1d012811e3..a6eca7eafc9c 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/data_quality/types.ts @@ -6,7 +6,11 @@ */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; + +export enum DataQualityEventTypes { + DataQualityIndexChecked = 'Data Quality Index Checked', + DataQualityCheckAllCompleted = 'Data Quality Check All Completed', +} export type ReportDataQualityIndexCheckedParams = ReportDataQualityCheckAllCompletedParams & { errorCount?: number; @@ -34,16 +38,12 @@ export interface ReportDataQualityCheckAllCompletedParams { timeConsumedMs?: number; } -export interface DataQualityTelemetryIndexCheckedEvent { - eventType: TelemetryEventTypes.DataQualityIndexChecked; - schema: RootSchema; +export interface DataQualityTelemetryEventsMap { + [DataQualityEventTypes.DataQualityIndexChecked]: ReportDataQualityIndexCheckedParams; + [DataQualityEventTypes.DataQualityCheckAllCompleted]: ReportDataQualityCheckAllCompletedParams; } -export interface DataQualityTelemetryCheckAllCompletedEvent { - eventType: TelemetryEventTypes.DataQualityCheckAllCompleted; - schema: RootSchema; +export interface DataQualityTelemetryEvents { + eventType: DataQualityEventTypes; + schema: RootSchema; } - -export type DataQualityTelemetryEvents = - | DataQualityTelemetryIndexCheckedEvent - | DataQualityTelemetryCheckAllCompletedEvent; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/index.ts index ba59cf5130dc..6cb27693464b 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { DocumentDetailsTelemetryEvent } from './types'; +import { DocumentEventTypes } from './types'; -export const DocumentDetailsFlyoutOpenedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.DetailsFlyoutOpened, +export const DocumentDetailsFlyoutOpenedEvent: DocumentDetailsTelemetryEvent = { + eventType: DocumentEventTypes.DetailsFlyoutOpened, schema: { location: { type: 'text', @@ -28,8 +28,8 @@ export const DocumentDetailsFlyoutOpenedEvent: TelemetryEvent = { }, }; -export const DocumentDetailsTabClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.DetailsFlyoutTabClicked, +export const DocumentDetailsTabClickedEvent: DocumentDetailsTelemetryEvent = { + eventType: DocumentEventTypes.DetailsFlyoutTabClicked, schema: { location: { type: 'text', @@ -54,3 +54,8 @@ export const DocumentDetailsTabClickedEvent: TelemetryEvent = { }, }, }; + +export const documentTelemetryEvents = [ + DocumentDetailsFlyoutOpenedEvent, + DocumentDetailsTabClickedEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/types.ts index 7a3ff374eae3..603b169e7740 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/document_details/types.ts @@ -6,29 +6,29 @@ */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface ReportDetailsFlyoutOpenedParams { +export enum DocumentEventTypes { + DetailsFlyoutOpened = 'Details Flyout Opened', + DetailsFlyoutTabClicked = 'Details Flyout Tabs Clicked', +} + +interface ReportDetailsFlyoutOpenedParams { location: string; panel: 'left' | 'right' | 'preview'; } -export interface ReportDetailsFlyoutTabClickedParams { +interface ReportDetailsFlyoutTabClickedParams { location: string; panel: 'left' | 'right'; tabId: string; } -export type ReportDocumentDetailsTelemetryEventParams = - | ReportDetailsFlyoutOpenedParams - | ReportDetailsFlyoutTabClickedParams; +export interface DocumentDetailsTelemetryEventsMap { + [DocumentEventTypes.DetailsFlyoutOpened]: ReportDetailsFlyoutOpenedParams; + [DocumentEventTypes.DetailsFlyoutTabClicked]: ReportDetailsFlyoutTabClickedParams; +} -export type DocumentDetailsTelemetryEvents = - | { - eventType: TelemetryEventTypes.DetailsFlyoutOpened; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.DetailsFlyoutTabClicked; - schema: RootSchema; - }; +export interface DocumentDetailsTelemetryEvent { + eventType: DocumentEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/index.ts index 5a45970de6af..771957d7a882 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { EntityAnalyticsTelemetryEvent } from './types'; +import { EntityEventTypes } from './types'; -export const entityClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.EntityDetailsClicked, +export const entityClickedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.EntityDetailsClicked, schema: { entity: { type: 'keyword', @@ -21,8 +21,8 @@ export const entityClickedEvent: TelemetryEvent = { }, }; -export const entityAlertsClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.EntityAlertsClicked, +export const entityAlertsClickedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.EntityAlertsClicked, schema: { entity: { type: 'keyword', @@ -34,8 +34,8 @@ export const entityAlertsClickedEvent: TelemetryEvent = { }, }; -export const entityRiskFilteredEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.EntityRiskFiltered, +export const entityRiskFilteredEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.EntityRiskFiltered, schema: { entity: { type: 'keyword', @@ -54,8 +54,8 @@ export const entityRiskFilteredEvent: TelemetryEvent = { }, }; -export const toggleRiskSummaryClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.ToggleRiskSummaryClicked, +export const toggleRiskSummaryClickedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.ToggleRiskSummaryClicked, schema: { entity: { type: 'keyword', @@ -74,8 +74,8 @@ export const toggleRiskSummaryClickedEvent: TelemetryEvent = { }, }; -export const RiskInputsExpandedFlyoutOpenedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.RiskInputsExpandedFlyoutOpened, +export const RiskInputsExpandedFlyoutOpenedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.RiskInputsExpandedFlyoutOpened, schema: { entity: { type: 'keyword', @@ -87,8 +87,8 @@ export const RiskInputsExpandedFlyoutOpenedEvent: TelemetryEvent = { }, }; -export const addRiskInputToTimelineClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AddRiskInputToTimelineClicked, +export const addRiskInputToTimelineClickedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.AddRiskInputToTimelineClicked, schema: { quantity: { type: 'integer', @@ -100,8 +100,8 @@ export const addRiskInputToTimelineClickedEvent: TelemetryEvent = { }, }; -export const assetCriticalityFileSelectedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssetCriticalityFileSelected, +export const assetCriticalityFileSelectedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.AssetCriticalityFileSelected, schema: { valid: { type: 'boolean', @@ -131,8 +131,8 @@ export const assetCriticalityFileSelectedEvent: TelemetryEvent = { }, }; -export const assetCriticalityCsvPreviewGeneratedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssetCriticalityCsvPreviewGenerated, +export const assetCriticalityCsvPreviewGeneratedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.AssetCriticalityCsvPreviewGenerated, schema: { file: { properties: { @@ -198,8 +198,8 @@ export const assetCriticalityCsvPreviewGeneratedEvent: TelemetryEvent = { }, }; -export const assetCriticalityCsvImportedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AssetCriticalityCsvImported, +export const assetCriticalityCsvImportedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.AssetCriticalityCsvImported, schema: { file: { properties: { @@ -215,8 +215,8 @@ export const assetCriticalityCsvImportedEvent: TelemetryEvent = { }, }; -export const entityStoreInitEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.EntityStoreDashboardInitButtonClicked, +export const entityStoreInitEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.EntityStoreDashboardInitButtonClicked, schema: { timestamp: { type: 'date', @@ -228,8 +228,8 @@ export const entityStoreInitEvent: TelemetryEvent = { }, }; -export const entityStoreEnablementEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.EntityStoreEnablementToggleClicked, +export const entityStoreEnablementEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.EntityStoreEnablementToggleClicked, schema: { timestamp: { type: 'date', @@ -247,3 +247,80 @@ export const entityStoreEnablementEvent: TelemetryEvent = { }, }, }; + +const mlJobUpdateEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.MLJobUpdate, + schema: { + jobId: { + type: 'keyword', + _meta: { + description: 'Job id', + optional: false, + }, + }, + isElasticJob: { + type: 'boolean', + _meta: { + description: 'If true the job is one of the pre-configure security solution modules', + optional: false, + }, + }, + moduleId: { + type: 'keyword', + _meta: { + description: 'Module id', + optional: true, + }, + }, + status: { + type: 'keyword', + _meta: { + description: 'It describes what has changed in the job.', + optional: false, + }, + }, + errorMessage: { + type: 'text', + _meta: { + description: 'Error message', + optional: true, + }, + }, + }, +}; + +const anomaliesCountClickedEvent: EntityAnalyticsTelemetryEvent = { + eventType: EntityEventTypes.AnomaliesCountClicked, + schema: { + jobId: { + type: 'keyword', + _meta: { + description: 'Job id', + optional: false, + }, + }, + count: { + type: 'integer', + _meta: { + description: 'Number of anomalies', + optional: false, + }, + }, + }, +}; + +export const entityTelemetryEvents = [ + entityClickedEvent, + entityAlertsClickedEvent, + entityRiskFilteredEvent, + assetCriticalityCsvPreviewGeneratedEvent, + assetCriticalityFileSelectedEvent, + assetCriticalityCsvImportedEvent, + entityStoreEnablementEvent, + entityStoreInitEvent, + toggleRiskSummaryClickedEvent, + RiskInputsExpandedFlyoutOpenedEvent, + addRiskInputToTimelineClickedEvent, + mlJobUpdateEvent, + anomaliesCountClickedEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/types.ts index 3313e99a3118..3051d675b6b1 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/entity_analytics/types.ts @@ -7,29 +7,52 @@ import type { RootSchema } from '@kbn/core/public'; import type { RiskSeverity } from '../../../../../../common/search_strategy'; -import type { TelemetryEventTypes } from '../../constants'; +export enum EntityEventTypes { + EntityDetailsClicked = 'Entity Details Clicked', + EntityAlertsClicked = 'Entity Alerts Clicked', + EntityRiskFiltered = 'Entity Risk Filtered', + EntityStoreEnablementToggleClicked = 'Entity Store Enablement Toggle Clicked', + EntityStoreDashboardInitButtonClicked = 'Entity Store Initialization Button Clicked', + ToggleRiskSummaryClicked = 'Toggle Risk Summary Clicked', + AddRiskInputToTimelineClicked = 'Add Risk Input To Timeline Clicked', + RiskInputsExpandedFlyoutOpened = 'Risk Inputs Expanded Flyout Opened', + AssetCriticalityCsvPreviewGenerated = 'Asset Criticality Csv Preview Generated', + AssetCriticalityFileSelected = 'Asset Criticality File Selected', + AssetCriticalityCsvImported = 'Asset Criticality CSV Imported', + AnomaliesCountClicked = 'Anomalies Count Clicked', + MLJobUpdate = 'ML Job Update', +} + +export enum ML_JOB_TELEMETRY_STATUS { + started = 'started', + startError = 'start_error', + stopped = 'stopped', + stopError = 'stop_error', + moduleInstalled = 'module_installed', + installationError = 'installationError', +} interface EntityParam { entity: 'host' | 'user'; } -export type ReportEntityDetailsClickedParams = EntityParam; -export type ReportEntityAlertsClickedParams = EntityParam; -export interface ReportEntityRiskFilteredParams extends Partial { +type ReportEntityDetailsClickedParams = EntityParam; +type ReportEntityAlertsClickedParams = EntityParam; +interface ReportEntityRiskFilteredParams extends Partial { selectedSeverity: RiskSeverity; } -export interface ReportToggleRiskSummaryClickedParams extends EntityParam { +interface ReportToggleRiskSummaryClickedParams extends EntityParam { action: 'show' | 'hide'; } -export type ReportRiskInputsExpandedFlyoutOpenedParams = EntityParam; +type ReportRiskInputsExpandedFlyoutOpenedParams = EntityParam; -export interface ReportAddRiskInputToTimelineClickedParams { +interface ReportAddRiskInputToTimelineClickedParams { quantity: number; } -export interface ReportAssetCriticalityFileSelectedParams { +interface ReportAssetCriticalityFileSelectedParams { valid: boolean; errorCode?: string; file: { @@ -37,7 +60,7 @@ export interface ReportAssetCriticalityFileSelectedParams { }; } -export interface ReportAssetCriticalityCsvPreviewGeneratedParams { +interface ReportAssetCriticalityCsvPreviewGeneratedParams { file: { size: number; }; @@ -53,76 +76,51 @@ export interface ReportAssetCriticalityCsvPreviewGeneratedParams { }; } -export interface ReportAssetCriticalityCsvImportedParams { +interface ReportAssetCriticalityCsvImportedParams { file: { size: number; }; } -export interface ReportEntityStoreEnablementParams { +interface ReportAnomaliesCountClickedParams { + jobId: string; + count: number; +} + +interface ReportEntityStoreEnablementParams { timestamp: string; action: 'start' | 'stop'; } -export interface ReportEntityStoreInitParams { +interface ReportEntityStoreInitParams { timestamp: string; } -export type ReportEntityAnalyticsTelemetryEventParams = - | ReportEntityDetailsClickedParams - | ReportEntityAlertsClickedParams - | ReportEntityRiskFilteredParams - | ReportToggleRiskSummaryClickedParams - | ReportRiskInputsExpandedFlyoutOpenedParams - | ReportAddRiskInputToTimelineClickedParams - | ReportAssetCriticalityCsvPreviewGeneratedParams - | ReportAssetCriticalityFileSelectedParams - | ReportAssetCriticalityCsvImportedParams - | ReportEntityStoreEnablementParams - | ReportEntityStoreInitParams; - -export type EntityAnalyticsTelemetryEvent = - | { - eventType: TelemetryEventTypes.EntityDetailsClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.EntityAlertsClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.EntityRiskFiltered; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AddRiskInputToTimelineClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.ToggleRiskSummaryClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.RiskInputsExpandedFlyoutOpened; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssetCriticalityCsvPreviewGenerated; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssetCriticalityFileSelected; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AssetCriticalityCsvImported; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.EntityStoreEnablementToggleClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.EntityStoreDashboardInitButtonClicked; - schema: RootSchema; - }; +interface ReportMLJobUpdateParams { + jobId: string; + isElasticJob: boolean; + status: ML_JOB_TELEMETRY_STATUS; + moduleId?: string; + errorMessage?: string; +} + +export interface EntityAnalyticsTelemetryEventsMap { + [EntityEventTypes.EntityDetailsClicked]: ReportEntityDetailsClickedParams; + [EntityEventTypes.EntityAlertsClicked]: ReportEntityAlertsClickedParams; + [EntityEventTypes.EntityRiskFiltered]: ReportEntityRiskFilteredParams; + [EntityEventTypes.EntityStoreEnablementToggleClicked]: ReportEntityStoreEnablementParams; + [EntityEventTypes.EntityStoreDashboardInitButtonClicked]: ReportEntityStoreInitParams; + [EntityEventTypes.ToggleRiskSummaryClicked]: ReportToggleRiskSummaryClickedParams; + [EntityEventTypes.AddRiskInputToTimelineClicked]: ReportAddRiskInputToTimelineClickedParams; + [EntityEventTypes.RiskInputsExpandedFlyoutOpened]: ReportRiskInputsExpandedFlyoutOpenedParams; + [EntityEventTypes.AssetCriticalityCsvPreviewGenerated]: ReportAssetCriticalityCsvPreviewGeneratedParams; + [EntityEventTypes.AssetCriticalityFileSelected]: ReportAssetCriticalityFileSelectedParams; + [EntityEventTypes.AssetCriticalityCsvImported]: ReportAssetCriticalityCsvImportedParams; + [EntityEventTypes.AnomaliesCountClicked]: ReportAnomaliesCountClickedParams; + [EntityEventTypes.MLJobUpdate]: ReportMLJobUpdateParams; +} + +export interface EntityAnalyticsTelemetryEvent { + eventType: EntityEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/index.ts index c30efcee10cf..7e34afa0aec6 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/index.ts @@ -6,10 +6,10 @@ */ import type { EventLogTelemetryEvent } from './types'; -import { TelemetryEventTypes } from '../../constants'; +import { EventLogEventTypes } from './types'; export const eventLogFilterByRunTypeEvent: EventLogTelemetryEvent = { - eventType: TelemetryEventTypes.EventLogFilterByRunType, + eventType: EventLogEventTypes.EventLogFilterByRunType, schema: { runType: { type: 'array', @@ -24,7 +24,7 @@ export const eventLogFilterByRunTypeEvent: EventLogTelemetryEvent = { }; export const eventLogShowSourceEventDateRangeEvent: EventLogTelemetryEvent = { - eventType: TelemetryEventTypes.EventLogShowSourceEventDateRange, + eventType: EventLogEventTypes.EventLogShowSourceEventDateRange, schema: { isVisible: { type: 'boolean', @@ -35,3 +35,8 @@ export const eventLogShowSourceEventDateRangeEvent: EventLogTelemetryEvent = { }, }, }; + +export const eventLogTelemetryEvents = [ + eventLogFilterByRunTypeEvent, + eventLogShowSourceEventDateRangeEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/types.ts index b196c9010b25..a2a32290ce40 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/event_log/types.ts @@ -5,25 +5,24 @@ * 2.0. */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface ReportEventLogFilterByRunTypeParams { +export enum EventLogEventTypes { + EventLogFilterByRunType = 'Event Log Filter By Run Type', + EventLogShowSourceEventDateRange = 'Event Log -> Show Source -> Event Date Range', +} +interface ReportEventLogFilterByRunTypeParams { runType: string[]; } -export interface ReportEventLogShowSourceEventDateRangeParams { +interface ReportEventLogShowSourceEventDateRangeParams { isVisible: boolean; } -export type ReportEventLogTelemetryEventParams = - | ReportEventLogFilterByRunTypeParams - | ReportEventLogShowSourceEventDateRangeParams; +export interface EventLogTelemetryEventsMap { + [EventLogEventTypes.EventLogFilterByRunType]: ReportEventLogFilterByRunTypeParams; + [EventLogEventTypes.EventLogShowSourceEventDateRange]: ReportEventLogShowSourceEventDateRangeParams; +} -export type EventLogTelemetryEvent = - | { - eventType: TelemetryEventTypes.EventLogFilterByRunType; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.EventLogShowSourceEventDateRange; - schema: RootSchema; - }; +export interface EventLogTelemetryEvent { + eventType: EventLogEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/index.ts index a1476944d980..3bc616dca1cf 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { ManualRuleRunTelemetryEvent } from './types'; +import { ManualRuleRunEventTypes } from './types'; -export const manualRuleRunOpenModalEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.ManualRuleRunOpenModal, +export const manualRuleRunOpenModalEvent: ManualRuleRunTelemetryEvent = { + eventType: ManualRuleRunEventTypes.ManualRuleRunOpenModal, schema: { type: { type: 'keyword', @@ -21,8 +21,8 @@ export const manualRuleRunOpenModalEvent: TelemetryEvent = { }, }; -export const manualRuleRunExecuteEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.ManualRuleRunExecute, +export const manualRuleRunExecuteEvent: ManualRuleRunTelemetryEvent = { + eventType: ManualRuleRunEventTypes.ManualRuleRunExecute, schema: { rangeInMs: { type: 'integer', @@ -50,8 +50,8 @@ export const manualRuleRunExecuteEvent: TelemetryEvent = { }, }; -export const manualRuleRunCancelJobEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.ManualRuleRunCancelJob, +export const manualRuleRunCancelJobEvent: ManualRuleRunTelemetryEvent = { + eventType: ManualRuleRunEventTypes.ManualRuleRunCancelJob, schema: { totalTasks: { type: 'integer', @@ -77,3 +77,9 @@ export const manualRuleRunCancelJobEvent: TelemetryEvent = { }, }, }; + +export const manualRuleRunTelemetryEvents = [ + manualRuleRunCancelJobEvent, + manualRuleRunExecuteEvent, + manualRuleRunOpenModalEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/types.ts index a58b0adf4550..231b555408e5 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/manual_rule_run/types.ts @@ -5,39 +5,35 @@ * 2.0. */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface ReportManualRuleRunOpenModalParams { +export enum ManualRuleRunEventTypes { + ManualRuleRunOpenModal = 'Manual Rule Run Open Modal', + ManualRuleRunExecute = 'Manual Rule Run Execute', + ManualRuleRunCancelJob = 'Manual Rule Run Cancel Job', +} +interface ReportManualRuleRunOpenModalParams { type: 'single' | 'bulk'; } -export interface ReportManualRuleRunExecuteParams { +interface ReportManualRuleRunExecuteParams { rangeInMs: number; rulesCount: number; status: 'success' | 'error'; } -export interface ReportManualRuleRunCancelJobParams { +interface ReportManualRuleRunCancelJobParams { totalTasks: number; completedTasks: number; errorTasks: number; } -export type ReportManualRuleRunTelemetryEventParams = - | ReportManualRuleRunOpenModalParams - | ReportManualRuleRunExecuteParams - | ReportManualRuleRunCancelJobParams; +export interface ManualRuleRunTelemetryEventsMap { + [ManualRuleRunEventTypes.ManualRuleRunOpenModal]: ReportManualRuleRunOpenModalParams; + [ManualRuleRunEventTypes.ManualRuleRunExecute]: ReportManualRuleRunExecuteParams; + [ManualRuleRunEventTypes.ManualRuleRunCancelJob]: ReportManualRuleRunCancelJobParams; +} -export type ManualRuleRunTelemetryEvent = - | { - eventType: TelemetryEventTypes.ManualRuleRunOpenModal; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.ManualRuleRunExecute; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.ManualRuleRunCancelJob; - schema: RootSchema; - }; +export interface ManualRuleRunTelemetryEvent { + eventType: ManualRuleRunEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/index.ts index c560f69730d3..94c9c350e010 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { NotesTelemetryEvent } from './types'; +import { NotesEventTypes } from './types'; -export const openNoteInExpandableFlyoutClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked, +export const openNoteInExpandableFlyoutClickedEvent: NotesTelemetryEvent = { + eventType: NotesEventTypes.OpenNoteInExpandableFlyoutClicked, schema: { location: { type: 'text', @@ -21,8 +21,8 @@ export const openNoteInExpandableFlyoutClickedEvent: TelemetryEvent = { }, }; -export const addNoteFromExpandableFlyoutClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked, +export const addNoteFromExpandableFlyoutClickedEvent: NotesTelemetryEvent = { + eventType: NotesEventTypes.AddNoteFromExpandableFlyoutClicked, schema: { isRelatedToATimeline: { type: 'boolean', @@ -33,3 +33,8 @@ export const addNoteFromExpandableFlyoutClickedEvent: TelemetryEvent = { }, }, }; + +export const notesTelemetryEvents = [ + openNoteInExpandableFlyoutClickedEvent, + addNoteFromExpandableFlyoutClickedEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/types.ts index a785f2f8493e..76440215c807 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/notes/types.ts @@ -6,26 +6,26 @@ */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface OpenNoteInExpandableFlyoutClickedParams { +interface OpenNoteInExpandableFlyoutClickedParams { location: string; } -export interface AddNoteFromExpandableFlyoutClickedParams { +interface AddNoteFromExpandableFlyoutClickedParams { isRelatedToATimeline: boolean; } -export type NotesTelemetryEventParams = - | OpenNoteInExpandableFlyoutClickedParams - | AddNoteFromExpandableFlyoutClickedParams; +export enum NotesEventTypes { + OpenNoteInExpandableFlyoutClicked = 'Open Note In Expandable Flyout Clicked', + AddNoteFromExpandableFlyoutClicked = 'Add Note From Expandable Flyout Clicked', +} + +export interface NotesTelemetryEventsMap { + [NotesEventTypes.OpenNoteInExpandableFlyoutClicked]: OpenNoteInExpandableFlyoutClickedParams; + [NotesEventTypes.AddNoteFromExpandableFlyoutClicked]: AddNoteFromExpandableFlyoutClickedParams; +} -export type NotesTelemetryEvents = - | { - eventType: TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked; - schema: RootSchema; - }; +export interface NotesTelemetryEvent { + eventType: NotesEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/index.ts index dacb0c948328..75a35e2d61c5 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { OnboardingHubTelemetryEvent } from './types'; +import { OnboardingHubEventTypes } from './types'; -export const onboardingHubStepOpenEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.OnboardingHubStepOpen, +export const onboardingHubStepOpenEvent: OnboardingHubTelemetryEvent = { + eventType: OnboardingHubEventTypes.OnboardingHubStepOpen, schema: { stepId: { type: 'keyword', @@ -28,8 +28,8 @@ export const onboardingHubStepOpenEvent: TelemetryEvent = { }, }; -export const onboardingHubStepLinkClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.OnboardingHubStepLinkClicked, +export const onboardingHubStepLinkClickedEvent: OnboardingHubTelemetryEvent = { + eventType: OnboardingHubEventTypes.OnboardingHubStepLinkClicked, schema: { originStepId: { type: 'keyword', @@ -48,8 +48,8 @@ export const onboardingHubStepLinkClickedEvent: TelemetryEvent = { }, }; -export const onboardingHubStepFinishedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.OnboardingHubStepFinished, +export const onboardingHubStepFinishedEvent: OnboardingHubTelemetryEvent = { + eventType: OnboardingHubEventTypes.OnboardingHubStepFinished, schema: { stepId: { type: 'keyword', @@ -74,3 +74,9 @@ export const onboardingHubStepFinishedEvent: TelemetryEvent = { }, }, }; + +export const onboardingHubTelemetryEvents = [ + onboardingHubStepOpenEvent, + onboardingHubStepLinkClickedEvent, + onboardingHubStepFinishedEvent, +]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/types.ts index 224635715b32..d11e9800e16f 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/onboarding/types.ts @@ -5,30 +5,25 @@ * 2.0. */ import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export type OnboardingHubStepOpenTrigger = 'navigation' | 'click'; +export enum OnboardingHubEventTypes { + OnboardingHubStepOpen = 'Onboarding Hub Step Open', + OnboardingHubStepFinished = 'Onboarding Hub Step Finished', + OnboardingHubStepLinkClicked = 'Onboarding Hub Step Link Clicked', +} + +type OnboardingHubStepOpenTrigger = 'navigation' | 'click'; -export interface OnboardingHubStepOpenParams { +interface OnboardingHubStepOpenParams { stepId: string; trigger: OnboardingHubStepOpenTrigger; } -export interface OnboardingHubStepOpen { - eventType: TelemetryEventTypes.OnboardingHubStepOpen; - schema: RootSchema; -} - export interface OnboardingHubStepLinkClickedParams { originStepId: string; stepLinkId: string; } -export interface OnboardingHubStepLinkClicked { - eventType: TelemetryEventTypes.OnboardingHubStepLinkClicked; - schema: RootSchema; -} - export type OnboardingHubStepFinishedTrigger = 'auto_check' | 'click'; export interface OnboardingHubStepFinishedParams { @@ -37,12 +32,13 @@ export interface OnboardingHubStepFinishedParams { trigger: OnboardingHubStepFinishedTrigger; } -export interface OnboardingHubStepFinished { - eventType: TelemetryEventTypes.OnboardingHubStepFinished; - schema: RootSchema; +export interface OnboardingHubTelemetryEventsMap { + [OnboardingHubEventTypes.OnboardingHubStepOpen]: OnboardingHubStepOpenParams; + [OnboardingHubEventTypes.OnboardingHubStepFinished]: OnboardingHubStepFinishedParams; + [OnboardingHubEventTypes.OnboardingHubStepLinkClicked]: OnboardingHubStepLinkClickedParams; } -export type OnboardingHubTelemetryEvent = - | OnboardingHubStepOpen - | OnboardingHubStepFinished - | OnboardingHubStepLinkClicked; +export interface OnboardingHubTelemetryEvent { + eventType: OnboardingHubEventTypes; + schema: RootSchema; +} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/index.ts index 12d721c45e2c..f34380935b0e 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/index.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { TelemetryEvent } from '../../types'; -import { TelemetryEventTypes } from '../../constants'; +import type { PreviewRuleTelemetryEvent } from './types'; +import { PreviewRuleEventTypes } from './types'; -export const previewRuleEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.PreviewRule, +export const previewRuleEvent: PreviewRuleTelemetryEvent = { + eventType: PreviewRuleEventTypes.PreviewRule, schema: { ruleType: { type: 'keyword', @@ -27,3 +27,5 @@ export const previewRuleEvent: TelemetryEvent = { }, }, }; + +export const previewRuleTelemetryEvents = [previewRuleEvent]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/types.ts index e5523080088f..876378e24553 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/preview_rule/types.ts @@ -7,14 +7,21 @@ import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import type { RootSchema } from '@kbn/core/public'; -import type { TelemetryEventTypes } from '../../constants'; -export interface PreviewRuleParams { +interface PreviewRuleParams { ruleType: Type; loggedRequestsEnabled: boolean; } +export enum PreviewRuleEventTypes { + PreviewRule = 'Preview rule', +} + +export interface PreviewRuleTelemetryEventsMap { + [PreviewRuleEventTypes.PreviewRule]: PreviewRuleParams; +} + export interface PreviewRuleTelemetryEvent { - eventType: TelemetryEventTypes.PreviewRule; - schema: RootSchema; + eventType: PreviewRuleEventTypes; + schema: RootSchema; } diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts index 3e7c9f113839..b610f6e77dda 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/events/telemetry_events.ts @@ -4,198 +4,28 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { TelemetryEvent } from '../types'; -import { TelemetryEventTypes } from '../constants'; -import { - alertsGroupingChangedEvent, - alertsGroupingTakeActionEvent, - alertsGroupingToggledEvent, -} from './alerts_grouping'; -import { - entityAlertsClickedEvent, - entityClickedEvent, - entityRiskFilteredEvent, - addRiskInputToTimelineClickedEvent, - RiskInputsExpandedFlyoutOpenedEvent, - toggleRiskSummaryClickedEvent, - assetCriticalityCsvPreviewGeneratedEvent, - assetCriticalityFileSelectedEvent, - assetCriticalityCsvImportedEvent, - entityStoreEnablementEvent, - entityStoreInitEvent, -} from './entity_analytics'; -import { - assistantInvokedEvent, - assistantSettingToggledEvent, - assistantMessageSentEvent, - assistantQuickPrompt, -} from './ai_assistant'; -import { dataQualityIndexCheckedEvent, dataQualityCheckAllClickedEvent } from './data_quality'; -import { - DocumentDetailsFlyoutOpenedEvent, - DocumentDetailsTabClickedEvent, -} from './document_details'; -import { - onboardingHubStepFinishedEvent, - onboardingHubStepLinkClickedEvent, - onboardingHubStepOpenEvent, -} from './onboarding'; -import { - manualRuleRunCancelJobEvent, - manualRuleRunExecuteEvent, - manualRuleRunOpenModalEvent, -} from './manual_rule_run'; -import { eventLogFilterByRunTypeEvent, eventLogShowSourceEventDateRangeEvent } from './event_log'; -import { - addNoteFromExpandableFlyoutClickedEvent, - openNoteInExpandableFlyoutClickedEvent, -} from './notes'; -import { previewRuleEvent } from './preview_rule'; - -const mlJobUpdateEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.MLJobUpdate, - schema: { - jobId: { - type: 'keyword', - _meta: { - description: 'Job id', - optional: false, - }, - }, - isElasticJob: { - type: 'boolean', - _meta: { - description: 'If true the job is one of the pre-configure security solution modules', - optional: false, - }, - }, - moduleId: { - type: 'keyword', - _meta: { - description: 'Module id', - optional: true, - }, - }, - status: { - type: 'keyword', - _meta: { - description: 'It describes what has changed in the job.', - optional: false, - }, - }, - errorMessage: { - type: 'text', - _meta: { - description: 'Error message', - optional: true, - }, - }, - }, -}; - -const cellActionClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.CellActionClicked, - schema: { - fieldName: { - type: 'keyword', - _meta: { - description: 'Field Name', - optional: false, - }, - }, - actionId: { - type: 'keyword', - _meta: { - description: 'Action id', - optional: false, - }, - }, - displayName: { - type: 'keyword', - _meta: { - description: 'User friendly action name', - optional: false, - }, - }, - metadata: { - type: 'pass_through', - _meta: { - description: 'Action metadata', - optional: true, - }, - }, - }, -}; - -const anomaliesCountClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.AnomaliesCountClicked, - schema: { - jobId: { - type: 'keyword', - _meta: { - description: 'Job id', - optional: false, - }, - }, - count: { - type: 'integer', - _meta: { - description: 'Number of anomalies', - optional: false, - }, - }, - }, -}; - -const breadCrumbClickedEvent: TelemetryEvent = { - eventType: TelemetryEventTypes.BreadcrumbClicked, - schema: { - title: { - type: 'keyword', - _meta: { - description: 'Breadcrumb title', - optional: false, - }, - }, - }, -}; +import { assistantTelemetryEvents } from './ai_assistant'; +import { alertsTelemetryEvents } from './alerts_grouping'; +import { appTelemetryEvents } from './app'; +import { dataQualityTelemetryEvents } from './data_quality'; +import { documentTelemetryEvents } from './document_details'; +import { entityTelemetryEvents } from './entity_analytics'; +import { eventLogTelemetryEvents } from './event_log'; +import { manualRuleRunTelemetryEvents } from './manual_rule_run'; +import { notesTelemetryEvents } from './notes'; +import { onboardingHubTelemetryEvents } from './onboarding'; +import { previewRuleTelemetryEvents } from './preview_rule'; export const telemetryEvents = [ - alertsGroupingToggledEvent, - alertsGroupingChangedEvent, - alertsGroupingTakeActionEvent, - assistantInvokedEvent, - assistantMessageSentEvent, - assistantQuickPrompt, - assistantSettingToggledEvent, - entityClickedEvent, - entityAlertsClickedEvent, - entityRiskFilteredEvent, - assetCriticalityCsvPreviewGeneratedEvent, - assetCriticalityFileSelectedEvent, - assetCriticalityCsvImportedEvent, - entityStoreEnablementEvent, - entityStoreInitEvent, - toggleRiskSummaryClickedEvent, - RiskInputsExpandedFlyoutOpenedEvent, - addRiskInputToTimelineClickedEvent, - mlJobUpdateEvent, - cellActionClickedEvent, - anomaliesCountClickedEvent, - dataQualityIndexCheckedEvent, - dataQualityCheckAllClickedEvent, - breadCrumbClickedEvent, - DocumentDetailsFlyoutOpenedEvent, - DocumentDetailsTabClickedEvent, - onboardingHubStepOpenEvent, - onboardingHubStepLinkClickedEvent, - onboardingHubStepFinishedEvent, - manualRuleRunCancelJobEvent, - manualRuleRunExecuteEvent, - manualRuleRunOpenModalEvent, - eventLogFilterByRunTypeEvent, - eventLogShowSourceEventDateRangeEvent, - openNoteInExpandableFlyoutClickedEvent, - addNoteFromExpandableFlyoutClickedEvent, - previewRuleEvent, + ...assistantTelemetryEvents, + ...alertsTelemetryEvents, + ...previewRuleTelemetryEvents, + ...entityTelemetryEvents, + ...dataQualityTelemetryEvents, + ...documentTelemetryEvents, + ...onboardingHubTelemetryEvents, + ...manualRuleRunTelemetryEvents, + ...eventLogTelemetryEvents, + ...notesTelemetryEvents, + ...appTelemetryEvents, ]; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/index.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/index.ts index 5a6818c712de..a8df452d512a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/index.ts @@ -5,23 +5,9 @@ * 2.0. */ -import type { AlertWorkflowStatus } from '../../types'; export { telemetryMiddleware } from './middleware'; export * from './constants'; -export * from './telemetry_client'; export * from './telemetry_service'; export * from './track'; export * from './types'; - -export const getTelemetryEvent = { - groupedAlertsTakeAction: ({ - tableId, - groupNumber, - status, - }: { - tableId: string; - groupNumber: number; - status: AlertWorkflowStatus; - }) => `alerts_table_${tableId}_group-${groupNumber}_mark-${status}`, -}; diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts deleted file mode 100644 index 87d4b215543d..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.mock.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { TelemetryClientStart } from './types'; - -export const createTelemetryClientMock = (): jest.Mocked => ({ - reportAlertsGroupingChanged: jest.fn(), - reportAlertsGroupingToggled: jest.fn(), - reportAlertsGroupingTakeAction: jest.fn(), - reportAssistantInvoked: jest.fn(), - reportAssistantMessageSent: jest.fn(), - reportAssistantQuickPrompt: jest.fn(), - reportAssistantSettingToggled: jest.fn(), - reportEntityDetailsClicked: jest.fn(), - reportEntityAlertsClicked: jest.fn(), - reportEntityRiskFiltered: jest.fn(), - reportMLJobUpdate: jest.fn(), - reportCellActionClicked: jest.fn(), - reportAnomaliesCountClicked: jest.fn(), - reportDataQualityIndexChecked: jest.fn(), - reportDataQualityCheckAllCompleted: jest.fn(), - reportBreadcrumbClicked: jest.fn(), - reportToggleRiskSummaryClicked: jest.fn(), - reportRiskInputsExpandedFlyoutOpened: jest.fn(), - reportAddRiskInputToTimelineClicked: jest.fn(), - reportDetailsFlyoutOpened: jest.fn(), - reportDetailsFlyoutTabClicked: jest.fn(), - reportOnboardingHubStepOpen: jest.fn(), - reportOnboardingHubStepLinkClicked: jest.fn(), - reportOnboardingHubStepFinished: jest.fn(), - reportAssetCriticalityCsvPreviewGenerated: jest.fn(), - reportAssetCriticalityFileSelected: jest.fn(), - reportAssetCriticalityCsvImported: jest.fn(), - reportEventLogFilterByRunType: jest.fn(), - reportEventLogShowSourceEventDateRange: jest.fn(), - reportManualRuleRunCancelJob: jest.fn(), - reportManualRuleRunExecute: jest.fn(), - reportManualRuleRunOpenModal: jest.fn(), - reportOpenNoteInExpandableFlyoutClicked: jest.fn(), - reportAddNoteFromExpandableFlyoutClicked: jest.fn(), - reportPreviewRule: jest.fn(), - reportEntityStoreEnablement: jest.fn(), - reportEntityStoreInit: jest.fn(), -}); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts deleted file mode 100644 index 689209f284db..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; -import type { - AddNoteFromExpandableFlyoutClickedParams, - OpenNoteInExpandableFlyoutClickedParams, -} from './events/notes/types'; -import type { - TelemetryClientStart, - ReportAlertsGroupingChangedParams, - ReportAlertsGroupingToggledParams, - ReportAlertsTakeActionParams, - ReportEntityDetailsClickedParams, - ReportEntityAlertsClickedParams, - ReportEntityRiskFilteredParams, - ReportMLJobUpdateParams, - ReportCellActionClickedParams, - ReportAnomaliesCountClickedParams, - ReportDataQualityIndexCheckedParams, - ReportDataQualityCheckAllCompletedParams, - ReportBreadcrumbClickedParams, - ReportAssistantInvokedParams, - ReportAssistantMessageSentParams, - ReportAssistantQuickPromptParams, - ReportAssistantSettingToggledParams, - ReportRiskInputsExpandedFlyoutOpenedParams, - ReportToggleRiskSummaryClickedParams, - ReportDetailsFlyoutOpenedParams, - ReportDetailsFlyoutTabClickedParams, - ReportAssetCriticalityCsvPreviewGeneratedParams, - ReportAssetCriticalityFileSelectedParams, - ReportAssetCriticalityCsvImportedParams, - ReportAddRiskInputToTimelineClickedParams, - OnboardingHubStepLinkClickedParams, - OnboardingHubStepOpenParams, - OnboardingHubStepFinishedParams, - ReportManualRuleRunCancelJobParams, - ReportManualRuleRunExecuteParams, - ReportManualRuleRunOpenModalParams, - ReportEventLogShowSourceEventDateRangeParams, - ReportEventLogFilterByRunTypeParams, - PreviewRuleParams, - ReportEntityStoreEnablementParams, - ReportEntityStoreInitParams, -} from './types'; -import { TelemetryEventTypes } from './constants'; - -/** - * Client which aggregate all the available telemetry tracking functions - * for the plugin - */ -export class TelemetryClient implements TelemetryClientStart { - constructor(private analytics: AnalyticsServiceSetup) {} - - public reportAlertsGroupingChanged = (params: ReportAlertsGroupingChangedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AlertsGroupingChanged, params); - }; - - public reportAlertsGroupingToggled = (params: ReportAlertsGroupingToggledParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AlertsGroupingToggled, params); - }; - - public reportAlertsGroupingTakeAction = (params: ReportAlertsTakeActionParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AlertsGroupingTakeAction, params); - }; - - public reportAssistantInvoked = (params: ReportAssistantInvokedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AssistantInvoked, params); - }; - - public reportAssistantMessageSent = (params: ReportAssistantMessageSentParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AssistantMessageSent, params); - }; - - public reportAssistantQuickPrompt = (params: ReportAssistantQuickPromptParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AssistantQuickPrompt, params); - }; - - public reportAssistantSettingToggled = (params: ReportAssistantSettingToggledParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AssistantSettingToggled, params); - }; - - public reportEntityDetailsClicked = ({ entity }: ReportEntityDetailsClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EntityDetailsClicked, { - entity, - }); - }; - - public reportEntityAlertsClicked = ({ entity }: ReportEntityAlertsClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EntityAlertsClicked, { - entity, - }); - }; - - public reportEntityRiskFiltered = ({ - entity, - selectedSeverity, - }: ReportEntityRiskFilteredParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EntityRiskFiltered, { - entity, - selectedSeverity, - }); - }; - - public reportAssetCriticalityCsvPreviewGenerated = ( - params: ReportAssetCriticalityCsvPreviewGeneratedParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.AssetCriticalityCsvPreviewGenerated, params); - }; - - public reportAssetCriticalityFileSelected = ( - params: ReportAssetCriticalityFileSelectedParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.AssetCriticalityFileSelected, params); - }; - - public reportAssetCriticalityCsvImported = (params: ReportAssetCriticalityCsvImportedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AssetCriticalityCsvImported, params); - }; - - public reportMLJobUpdate = (params: ReportMLJobUpdateParams) => { - this.analytics.reportEvent(TelemetryEventTypes.MLJobUpdate, params); - }; - - reportToggleRiskSummaryClicked(params: ReportToggleRiskSummaryClickedParams): void { - this.analytics.reportEvent(TelemetryEventTypes.ToggleRiskSummaryClicked, params); - } - reportRiskInputsExpandedFlyoutOpened(params: ReportRiskInputsExpandedFlyoutOpenedParams): void { - this.analytics.reportEvent(TelemetryEventTypes.RiskInputsExpandedFlyoutOpened, params); - } - reportAddRiskInputToTimelineClicked(params: ReportAddRiskInputToTimelineClickedParams): void { - this.analytics.reportEvent(TelemetryEventTypes.AddRiskInputToTimelineClicked, params); - } - - public reportCellActionClicked = (params: ReportCellActionClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.CellActionClicked, params); - }; - - public reportAnomaliesCountClicked = (params: ReportAnomaliesCountClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.AnomaliesCountClicked, params); - }; - - public reportDataQualityIndexChecked = (params: ReportDataQualityIndexCheckedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.DataQualityIndexChecked, params); - }; - - public reportDataQualityCheckAllCompleted = ( - params: ReportDataQualityCheckAllCompletedParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.DataQualityCheckAllCompleted, params); - }; - - public reportBreadcrumbClicked = ({ title }: ReportBreadcrumbClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.BreadcrumbClicked, { - title, - }); - }; - - public reportDetailsFlyoutOpened = (params: ReportDetailsFlyoutOpenedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.DetailsFlyoutOpened, params); - }; - - public reportDetailsFlyoutTabClicked = (params: ReportDetailsFlyoutTabClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.DetailsFlyoutTabClicked, params); - }; - - public reportOnboardingHubStepOpen = (params: OnboardingHubStepOpenParams) => { - this.analytics.reportEvent(TelemetryEventTypes.OnboardingHubStepOpen, params); - }; - - public reportOnboardingHubStepFinished = (params: OnboardingHubStepFinishedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.OnboardingHubStepFinished, params); - }; - - public reportOnboardingHubStepLinkClicked = (params: OnboardingHubStepLinkClickedParams) => { - this.analytics.reportEvent(TelemetryEventTypes.OnboardingHubStepLinkClicked, params); - }; - - public reportManualRuleRunOpenModal = (params: ReportManualRuleRunOpenModalParams) => { - this.analytics.reportEvent(TelemetryEventTypes.ManualRuleRunOpenModal, params); - }; - - public reportManualRuleRunExecute = (params: ReportManualRuleRunExecuteParams) => { - this.analytics.reportEvent(TelemetryEventTypes.ManualRuleRunExecute, params); - }; - - public reportManualRuleRunCancelJob = (params: ReportManualRuleRunCancelJobParams) => { - this.analytics.reportEvent(TelemetryEventTypes.ManualRuleRunCancelJob, params); - }; - - public reportEventLogFilterByRunType = (params: ReportEventLogFilterByRunTypeParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EventLogFilterByRunType, params); - }; - - public reportEventLogShowSourceEventDateRange( - params: ReportEventLogShowSourceEventDateRangeParams - ): void { - this.analytics.reportEvent(TelemetryEventTypes.EventLogShowSourceEventDateRange, params); - } - - public reportOpenNoteInExpandableFlyoutClicked = ( - params: OpenNoteInExpandableFlyoutClickedParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked, params); - }; - - public reportAddNoteFromExpandableFlyoutClicked = ( - params: AddNoteFromExpandableFlyoutClickedParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked, params); - }; - - public reportPreviewRule = (params: PreviewRuleParams) => { - this.analytics.reportEvent(TelemetryEventTypes.PreviewRule, params); - }; - - public reportEntityStoreEnablement = (params: ReportEntityStoreEnablementParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EntityStoreEnablementToggleClicked, params); - }; - - public reportEntityStoreInit = (params: ReportEntityStoreInitParams) => { - this.analytics.reportEvent(TelemetryEventTypes.EntityStoreDashboardInitButtonClicked, params); - }; -} diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.mock.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.mock.ts index 519ba4527560..30b8a0c434c5 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.mock.ts @@ -5,6 +5,4 @@ * 2.0. */ -import { createTelemetryClientMock } from './telemetry_client.mock'; - -export const createTelemetryServiceMock = () => createTelemetryClientMock(); +export const createTelemetryServiceMock = () => ({ reportEvent: jest.fn() }); diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.test.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.test.ts index 9079c6bf4f65..486aa241290a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.test.ts @@ -8,7 +8,7 @@ import { coreMock } from '@kbn/core/server/mocks'; import { telemetryEvents } from './events/telemetry_events'; import { TelemetryService } from './telemetry_service'; -import { TelemetryEventTypes } from './constants'; +import { AlertsEventTypes } from './types'; describe('TelemetryService', () => { let service: TelemetryService; @@ -41,17 +41,12 @@ describe('TelemetryService', () => { }); describe('#start()', () => { - it('should return all the available tracking methods', () => { + it('should return the tracking method', () => { const setupParams = getSetupParams(); service.setup(setupParams); const telemetry = service.start(); - expect(telemetry).toHaveProperty('reportAlertsGroupingChanged'); - expect(telemetry).toHaveProperty('reportAlertsGroupingToggled'); - expect(telemetry).toHaveProperty('reportAlertsGroupingTakeAction'); - - expect(telemetry).toHaveProperty('reportDetailsFlyoutOpened'); - expect(telemetry).toHaveProperty('reportDetailsFlyoutTabClicked'); + expect(telemetry).toHaveProperty('reportEvent'); }); }); @@ -61,7 +56,7 @@ describe('TelemetryService', () => { service.setup(setupParams); const telemetry = service.start(); - telemetry.reportAlertsGroupingTakeAction({ + telemetry.reportEvent(AlertsEventTypes.AlertsGroupingTakeAction, { tableId: 'test-groupingId', groupNumber: 0, status: 'closed', @@ -70,7 +65,7 @@ describe('TelemetryService', () => { expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( - TelemetryEventTypes.AlertsGroupingTakeAction, + AlertsEventTypes.AlertsGroupingTakeAction, { tableId: 'test-groupingId', groupNumber: 0, diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts index d4c100d5fe40..a1bf49394af9 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts @@ -8,13 +8,18 @@ import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import { of } from 'rxjs'; import type { + TelemetryEventTypeData, + TelemetryEventTypes, TelemetryServiceSetupParams, - TelemetryClientStart, - TelemetryEventParams, } from './types'; import { telemetryEvents } from './events/telemetry_events'; -import { TelemetryClient } from './telemetry_client'; +export interface TelemetryServiceStart { + reportEvent: ( + eventType: T, + eventData: TelemetryEventTypeData + ) => void; +} /** * Service that interacts with the Core's analytics module * to trigger custom event for Security Solution plugin features @@ -41,17 +46,19 @@ export class TelemetryService { }); } telemetryEvents.forEach((eventConfig) => - analytics.registerEventType(eventConfig) + analytics.registerEventType>(eventConfig) ); } - public start(): TelemetryClientStart { - if (!this.analytics) { + public start(): TelemetryServiceStart { + const reportEvent = this.analytics?.reportEvent.bind(this.analytics); + + if (!this.analytics || !reportEvent) { throw new Error( 'The TelemetryService.setup() method has not been invoked, be sure to call it during the plugin setup.' ); } - return new TelemetryClient(this.analytics); + return { reportEvent }; } } diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts index 95896bf74a6a..9cd56ebcb60f 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/types.ts @@ -5,77 +5,41 @@ * 2.0. */ -import type { AnalyticsServiceSetup, RootSchema } from '@kbn/core/public'; -import type { SecurityCellActionMetadata } from '../../../app/actions/types'; -import type { ML_JOB_TELEMETRY_STATUS, TelemetryEventTypes } from './constants'; +import type { AnalyticsServiceSetup } from '@kbn/core/public'; import type { - AlertsGroupingTelemetryEvent, - ReportAlertsGroupingChangedParams, - ReportAlertsGroupingTelemetryEventParams, - ReportAlertsGroupingToggledParams, - ReportAlertsTakeActionParams, + AlertsEventTypes, + AlertsGroupingTelemetryEventsMap, } from './events/alerts_grouping/types'; import type { - ReportDataQualityCheckAllCompletedParams, - ReportDataQualityIndexCheckedParams, - DataQualityTelemetryEvents, + DataQualityEventTypes, + DataQualityTelemetryEventsMap, } from './events/data_quality/types'; import type { - EntityAnalyticsTelemetryEvent, - ReportAddRiskInputToTimelineClickedParams, - ReportEntityAlertsClickedParams, - ReportEntityAnalyticsTelemetryEventParams, - ReportEntityDetailsClickedParams, - ReportEntityRiskFilteredParams, - ReportRiskInputsExpandedFlyoutOpenedParams, - ReportToggleRiskSummaryClickedParams, - ReportAssetCriticalityCsvPreviewGeneratedParams, - ReportAssetCriticalityFileSelectedParams, - ReportAssetCriticalityCsvImportedParams, - ReportEntityStoreEnablementParams, - ReportEntityStoreInitParams, + EntityAnalyticsTelemetryEventsMap, + EntityEventTypes, } from './events/entity_analytics/types'; +import type { AssistantEventTypes, AssistantTelemetryEventsMap } from './events/ai_assistant/types'; import type { - AssistantTelemetryEvent, - ReportAssistantTelemetryEventParams, - ReportAssistantInvokedParams, - ReportAssistantQuickPromptParams, - ReportAssistantMessageSentParams, - ReportAssistantSettingToggledParams, -} from './events/ai_assistant/types'; -import type { - DocumentDetailsTelemetryEvents, - ReportDocumentDetailsTelemetryEventParams, - ReportDetailsFlyoutOpenedParams, - ReportDetailsFlyoutTabClickedParams, + DocumentDetailsTelemetryEventsMap, + DocumentEventTypes, } from './events/document_details/types'; import type { - OnboardingHubStepFinishedParams, - OnboardingHubStepLinkClickedParams, - OnboardingHubStepOpenParams, - OnboardingHubTelemetryEvent, + OnboardingHubEventTypes, + OnboardingHubTelemetryEventsMap, } from './events/onboarding/types'; import type { - ManualRuleRunTelemetryEvent, - ReportManualRuleRunOpenModalParams, - ReportManualRuleRunExecuteParams, - ReportManualRuleRunCancelJobParams, - ReportManualRuleRunTelemetryEventParams, + ManualRuleRunEventTypes, + ManualRuleRunTelemetryEventsMap, } from './events/manual_rule_run/types'; +import type { EventLogEventTypes, EventLogTelemetryEventsMap } from './events/event_log/types'; +import type { NotesEventTypes, NotesTelemetryEventsMap } from './events/notes/types'; import type { - EventLogTelemetryEvent, - ReportEventLogFilterByRunTypeParams, - ReportEventLogShowSourceEventDateRangeParams, - ReportEventLogTelemetryEventParams, -} from './events/event_log/types'; -import type { - AddNoteFromExpandableFlyoutClickedParams, - NotesTelemetryEventParams, - NotesTelemetryEvents, - OpenNoteInExpandableFlyoutClickedParams, -} from './events/notes/types'; -import type { PreviewRuleParams, PreviewRuleTelemetryEvent } from './events/preview_rule/types'; + PreviewRuleEventTypes, + PreviewRuleTelemetryEventsMap, +} from './events/preview_rule/types'; +import type { AppEventTypes, AppTelemetryEventsMap } from './events/app/types'; +export * from './events/app/types'; export * from './events/ai_assistant/types'; export * from './events/alerts_grouping/types'; export * from './events/data_quality/types'; @@ -85,142 +49,46 @@ export * from './events/document_details/types'; export * from './events/manual_rule_run/types'; export * from './events/event_log/types'; export * from './events/preview_rule/types'; +export * from './events/notes/types'; export interface TelemetryServiceSetupParams { analytics: AnalyticsServiceSetup; } -export interface ReportMLJobUpdateParams { - jobId: string; - isElasticJob: boolean; - status: ML_JOB_TELEMETRY_STATUS; - moduleId?: string; - errorMessage?: string; -} - -export interface ReportCellActionClickedParams { - metadata: SecurityCellActionMetadata | undefined; - displayName: string; - actionId: string; - fieldName: string; -} - -export interface ReportAnomaliesCountClickedParams { - jobId: string; - count: number; -} - -export interface ReportBreadcrumbClickedParams { - title: string; -} - -export type TelemetryEventParams = - | ReportAlertsGroupingTelemetryEventParams - | ReportAssistantTelemetryEventParams - | ReportEntityAnalyticsTelemetryEventParams - | ReportMLJobUpdateParams - | ReportCellActionClickedParams - | ReportAnomaliesCountClickedParams - | ReportDataQualityIndexCheckedParams - | ReportDataQualityCheckAllCompletedParams - | ReportBreadcrumbClickedParams - | ReportDocumentDetailsTelemetryEventParams - | OnboardingHubStepOpenParams - | OnboardingHubStepFinishedParams - | OnboardingHubStepLinkClickedParams - | ReportManualRuleRunTelemetryEventParams - | ReportEventLogTelemetryEventParams - | PreviewRuleParams - | NotesTelemetryEventParams; - -export interface TelemetryClientStart { - reportAlertsGroupingChanged(params: ReportAlertsGroupingChangedParams): void; - reportAlertsGroupingToggled(params: ReportAlertsGroupingToggledParams): void; - reportAlertsGroupingTakeAction(params: ReportAlertsTakeActionParams): void; - - // Assistant - reportAssistantInvoked(params: ReportAssistantInvokedParams): void; - reportAssistantMessageSent(params: ReportAssistantMessageSentParams): void; - reportAssistantQuickPrompt(params: ReportAssistantQuickPromptParams): void; - reportAssistantSettingToggled(params: ReportAssistantSettingToggledParams): void; - - // Entity Analytics - reportEntityDetailsClicked(params: ReportEntityDetailsClickedParams): void; - reportEntityAlertsClicked(params: ReportEntityAlertsClickedParams): void; - reportEntityRiskFiltered(params: ReportEntityRiskFilteredParams): void; - reportMLJobUpdate(params: ReportMLJobUpdateParams): void; - // Entity Analytics inside Entity Flyout - reportToggleRiskSummaryClicked(params: ReportToggleRiskSummaryClickedParams): void; - reportRiskInputsExpandedFlyoutOpened(params: ReportRiskInputsExpandedFlyoutOpenedParams): void; - reportAddRiskInputToTimelineClicked(params: ReportAddRiskInputToTimelineClickedParams): void; - // Entity Analytics Asset Criticality - reportAssetCriticalityFileSelected(params: ReportAssetCriticalityFileSelectedParams): void; - reportAssetCriticalityCsvPreviewGenerated( - params: ReportAssetCriticalityCsvPreviewGeneratedParams - ): void; - reportAssetCriticalityCsvImported(params: ReportAssetCriticalityCsvImportedParams): void; - reportCellActionClicked(params: ReportCellActionClickedParams): void; - // Entity Analytics Entity Store - reportEntityStoreEnablement(params: ReportEntityStoreEnablementParams): void; - reportEntityStoreInit(params: ReportEntityStoreInitParams): void; - - reportAnomaliesCountClicked(params: ReportAnomaliesCountClickedParams): void; - reportDataQualityIndexChecked(params: ReportDataQualityIndexCheckedParams): void; - reportDataQualityCheckAllCompleted(params: ReportDataQualityCheckAllCompletedParams): void; - reportBreadcrumbClicked(params: ReportBreadcrumbClickedParams): void; - - // document details flyout - reportDetailsFlyoutOpened(params: ReportDetailsFlyoutOpenedParams): void; - reportDetailsFlyoutTabClicked(params: ReportDetailsFlyoutTabClickedParams): void; - - // onboarding hub - reportOnboardingHubStepOpen(params: OnboardingHubStepOpenParams): void; - reportOnboardingHubStepFinished(params: OnboardingHubStepFinishedParams): void; - reportOnboardingHubStepLinkClicked(params: OnboardingHubStepLinkClickedParams): void; - - // manual rule run - reportManualRuleRunOpenModal(params: ReportManualRuleRunOpenModalParams): void; - reportManualRuleRunExecute(params: ReportManualRuleRunExecuteParams): void; - reportManualRuleRunCancelJob(params: ReportManualRuleRunCancelJobParams): void; - - // event log - reportEventLogFilterByRunType(params: ReportEventLogFilterByRunTypeParams): void; - reportEventLogShowSourceEventDateRange( - params: ReportEventLogShowSourceEventDateRangeParams - ): void; - - // new notes - reportOpenNoteInExpandableFlyoutClicked(params: OpenNoteInExpandableFlyoutClickedParams): void; - reportAddNoteFromExpandableFlyoutClicked(params: AddNoteFromExpandableFlyoutClickedParams): void; - - // preview rule - reportPreviewRule(params: PreviewRuleParams): void; -} - -export type TelemetryEvent = - | AssistantTelemetryEvent - | AlertsGroupingTelemetryEvent - | EntityAnalyticsTelemetryEvent - | DataQualityTelemetryEvents - | DocumentDetailsTelemetryEvents - | { - eventType: TelemetryEventTypes.MLJobUpdate; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.CellActionClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.AnomaliesCountClicked; - schema: RootSchema; - } - | { - eventType: TelemetryEventTypes.BreadcrumbClicked; - schema: RootSchema; - } - | OnboardingHubTelemetryEvent - | ManualRuleRunTelemetryEvent - | EventLogTelemetryEvent - | PreviewRuleTelemetryEvent - | NotesTelemetryEvents; +// Combine all event type data +export type TelemetryEventTypeData = T extends AssistantEventTypes + ? AssistantTelemetryEventsMap[T] + : T extends AlertsEventTypes + ? AlertsGroupingTelemetryEventsMap[T] + : T extends PreviewRuleEventTypes + ? PreviewRuleTelemetryEventsMap[T] + : T extends EntityEventTypes + ? EntityAnalyticsTelemetryEventsMap[T] + : T extends DataQualityEventTypes + ? DataQualityTelemetryEventsMap[T] + : T extends DocumentEventTypes + ? DocumentDetailsTelemetryEventsMap[T] + : T extends OnboardingHubEventTypes + ? OnboardingHubTelemetryEventsMap[T] + : T extends ManualRuleRunEventTypes + ? ManualRuleRunTelemetryEventsMap[T] + : T extends EventLogEventTypes + ? EventLogTelemetryEventsMap[T] + : T extends NotesEventTypes + ? NotesTelemetryEventsMap[T] + : T extends AppEventTypes + ? AppTelemetryEventsMap[T] + : never; + +export type TelemetryEventTypes = + | AssistantEventTypes + | AlertsEventTypes + | PreviewRuleEventTypes + | EntityEventTypes + | DataQualityEventTypes + | DocumentEventTypes + | OnboardingHubEventTypes + | ManualRuleRunEventTypes + | EventLogEventTypes + | NotesEventTypes + | AppEventTypes; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/use_preview_rule.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/use_preview_rule.ts index 018e2602aa17..79257b4fef3d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/use_preview_rule.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/use_preview_rule.ts @@ -18,6 +18,7 @@ import { transformOutput } from '../../../../detections/containers/detection_eng import type { TimeframePreviewOptions } from '../../../../detections/pages/detection_engine/rules/types'; import { usePreviewInvocationCount } from './use_preview_invocation_count'; import * as i18n from './translations'; +import { PreviewRuleEventTypes } from '../../../../common/lib/telemetry'; const emptyPreviewRule: RulePreviewResponse = { previewId: undefined, @@ -58,7 +59,7 @@ export const usePreviewRule = ({ const createPreviewId = async () => { if (rule != null) { try { - telemetry.reportPreviewRule({ + telemetry.reportEvent(PreviewRuleEventTypes.PreviewRule, { loggedRequestsEnabled: enableLoggedRequests ?? false, ruleType: rule.type, }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.test.tsx index 4ed02135143f..9a9cb5771335 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.test.tsx @@ -32,7 +32,7 @@ jest.mock('../../../../../common/hooks/use_experimental_features', () => { }); const mockTelemetry = { - reportEventLogShowSourceEventDateRange: jest.fn(), + reportEvent: jest.fn(), }; const mockedUseKibana = { @@ -91,6 +91,6 @@ describe('ExecutionLogTable', () => { fireEvent.click(switchButton); - expect(mockTelemetry.reportEventLogShowSourceEventDateRange).toHaveBeenCalled(); + expect(mockTelemetry.reportEvent).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 4546e55522ce..296323213a6d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -85,6 +85,7 @@ import { getSourceEventTimeRangeColumns, } from './execution_log_columns'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; +import { EventLogEventTypes } from '../../../../../common/lib/telemetry'; const EXECUTION_UUID_FIELD_NAME = 'kibana.alert.rule.execution.uuid'; @@ -470,7 +471,7 @@ const ExecutionLogTableComponent: React.FC = ({ (e: EuiSwitchEvent) => { const isVisible = e.target.checked; onShowSourceEventTimeRange(isVisible); - telemetry.reportEventLogShowSourceEventDateRange({ + telemetry.reportEvent(EventLogEventTypes.EventLogShowSourceEventDateRange, { isVisible, }); }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.test.tsx index b2cdd83d6f43..faf19fd5dd24 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.test.tsx @@ -14,6 +14,7 @@ import { TestProviders } from '../../../../common/mock'; import { useKibana } from '../../../../common/lib/kibana'; import * as i18n from '../../translations'; import type { BackfillRow } from '../../types'; +import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry'; jest.mock('../../../../common/hooks/use_app_toasts'); jest.mock('../../api/hooks/use_delete_backfill'); @@ -25,7 +26,7 @@ const mockUseKibana = useKibana as jest.Mock; describe('StopBackfill', () => { const mockTelemetry = { - reportManualRuleRunCancelJob: jest.fn(), + reportEvent: jest.fn(), }; const addSuccess = jest.fn(); @@ -90,11 +91,14 @@ describe('StopBackfill', () => { fireEvent.click(getByTestId('confirmModalConfirmButton')); await waitFor(() => { - expect(mockTelemetry.reportManualRuleRunCancelJob).toHaveBeenCalledWith({ - totalTasks: backfill.total, - completedTasks: backfill.complete, - errorTasks: backfill.error, - }); + expect(mockTelemetry.reportEvent).toHaveBeenCalledWith( + ManualRuleRunEventTypes.ManualRuleRunCancelJob, + { + totalTasks: backfill.total, + completedTasks: backfill.complete, + errorTasks: backfill.error, + } + ); }); expect(addSuccess).toHaveBeenCalledWith(i18n.BACKFILLS_TABLE_STOP_CONFIRMATION_SUCCESS); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.tsx index 84acf0b014d6..51d09dc323d8 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/components/rule_backfills_info/stop_backfill.tsx @@ -12,6 +12,7 @@ import { useDeleteBackfill } from '../../api/hooks/use_delete_backfill'; import * as i18n from '../../translations'; import type { BackfillRow } from '../../types'; import { useKibana } from '../../../../common/lib/kibana'; +import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry'; export const StopBackfill = ({ backfill }: { backfill: BackfillRow }) => { const { telemetry } = useKibana().services; @@ -19,7 +20,7 @@ export const StopBackfill = ({ backfill }: { backfill: BackfillRow }) => { const deleteBackfillMutation = useDeleteBackfill({ onSuccess: () => { closeModal(); - telemetry.reportManualRuleRunCancelJob({ + telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunCancelJob, { totalTasks: backfill.total, completedTasks: backfill.complete, errorTasks: backfill.error, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.test.tsx index 36bdf8a8bf82..ce70bc08bd72 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.test.tsx @@ -11,6 +11,7 @@ import { useKibana } from '../../../common/lib/kibana'; import { useKibana as mockUseKibana } from '../../../common/lib/kibana/__mocks__'; import { TestProviders } from '../../../common/mock'; import { useScheduleRuleRun } from './use_schedule_rule_run'; +import { ManualRuleRunEventTypes } from '../../../common/lib/telemetry'; const mockUseScheduleRuleRunMutation = jest.fn(); @@ -28,7 +29,7 @@ const mockedUseKibana = { services: { ...mockUseKibana().services, telemetry: { - reportManualRuleRunExecute: jest.fn(), + reportEvent: jest.fn(), }, }, }; @@ -61,7 +62,7 @@ describe('When using the `useScheduleRuleRun()` hook', () => { ); }); - it('should call reportManualRuleRunExecute with success status on success', async () => { + it('should call reportEvent with success status on success', async () => { const { result, waitFor } = renderHook(() => useScheduleRuleRun(), { wrapper: TestProviders, }); @@ -77,14 +78,17 @@ describe('When using the `useScheduleRuleRun()` hook', () => { return mockUseScheduleRuleRunMutation.mock.calls.length > 0; }); - expect(mockedUseKibana.services.telemetry.reportManualRuleRunExecute).toHaveBeenCalledWith({ - rangeInMs: timeRange.endDate.diff(timeRange.startDate), - status: 'success', - rulesCount: 1, - }); + expect(mockedUseKibana.services.telemetry.reportEvent).toHaveBeenCalledWith( + ManualRuleRunEventTypes.ManualRuleRunExecute, + { + rangeInMs: timeRange.endDate.diff(timeRange.startDate), + status: 'success', + rulesCount: 1, + } + ); }); - it('should call reportManualRuleRunExecute with error status on failure', async () => { + it('should call reportEvent with error status on failure', async () => { const { result, waitFor } = renderHook(() => useScheduleRuleRun(), { wrapper: TestProviders, }); @@ -100,10 +104,13 @@ describe('When using the `useScheduleRuleRun()` hook', () => { return mockUseScheduleRuleRunMutation.mock.calls.length > 0; }); - expect(mockedUseKibana.services.telemetry.reportManualRuleRunExecute).toHaveBeenCalledWith({ - rangeInMs: timeRange.endDate.diff(timeRange.startDate), - status: 'error', - rulesCount: 1, - }); + expect(mockedUseKibana.services.telemetry.reportEvent).toHaveBeenCalledWith( + ManualRuleRunEventTypes.ManualRuleRunExecute, + { + rangeInMs: timeRange.endDate.diff(timeRange.startDate), + status: 'error', + rulesCount: 1, + } + ); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.ts index 7599d8685d3c..94f85a30b3ce 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_gaps/logic/use_schedule_rule_run.ts @@ -12,6 +12,7 @@ import { useScheduleRuleRunMutation } from '../api/hooks/use_schedule_rule_run_m import type { ScheduleBackfillProps } from '../types'; import * as i18n from '../translations'; +import { ManualRuleRunEventTypes } from '../../../common/lib/telemetry'; export function useScheduleRuleRun() { const { mutateAsync } = useScheduleRuleRunMutation(); @@ -22,7 +23,7 @@ export function useScheduleRuleRun() { async (options: ScheduleBackfillProps) => { try { const results = await mutateAsync(options); - telemetry.reportManualRuleRunExecute({ + telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunExecute, { rangeInMs: options.timeRange.endDate.diff(options.timeRange.startDate), status: 'success', rulesCount: options.ruleIds.length, @@ -31,7 +32,7 @@ export function useScheduleRuleRun() { return results; } catch (error) { addError(error, { title: i18n.BACKFILL_SCHEDULE_ERROR_TITLE }); - telemetry.reportManualRuleRunExecute({ + telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunExecute, { rangeInMs: options.timeRange.endDate.diff(options.timeRange.startDate), status: 'error', rulesCount: options.ruleIds.length, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx index 68e58b4db073..22f10605b368 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/use_bulk_actions.tsx @@ -45,6 +45,7 @@ import type { ExecuteBulkActionsDryRun } from './use_bulk_actions_dry_run'; import { computeDryRunEditPayload } from './utils/compute_dry_run_edit_payload'; import { transformExportDetailsToDryRunResult } from './utils/dry_run_result'; import { prepareSearchParams } from './utils/prepare_search_params'; +import { ManualRuleRunEventTypes } from '../../../../../common/lib/telemetry'; interface UseBulkActionsArgs { filterOptions: FilterOptions; @@ -234,7 +235,7 @@ export const useBulkActions = ({ } const modalManualRuleRunConfirmationResult = await showManualRuleRunConfirmation(); - startServices.telemetry.reportManualRuleRunOpenModal({ + startServices.telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunOpenModal, { type: 'bulk', }); if (modalManualRuleRunConfirmationResult === null) { @@ -252,7 +253,7 @@ export const useBulkActions = ({ }, }); - startServices.telemetry.reportManualRuleRunExecute({ + startServices.telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunExecute, { rangeInMs: modalManualRuleRunConfirmationResult.endDate.diff( modalManualRuleRunConfirmationResult.startDate ), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx index 4cc7a0342665..f3d0930d7c1f 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rules_table_actions.tsx @@ -25,6 +25,7 @@ import { useDownloadExportedRules } from '../../../rule_management/logic/bulk_ac import { useHasActionsPrivileges } from './use_has_actions_privileges'; import type { TimeRange } from '../../../rule_gaps/types'; import { useScheduleRuleRun } from '../../../rule_gaps/logic/use_schedule_rule_run'; +import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry'; export const useRulesTableActions = ({ showExceptionsDuplicateConfirmation, @@ -126,7 +127,7 @@ export const useRulesTableActions = ({ onClick: async (rule: Rule) => { startTransaction({ name: SINGLE_RULE_ACTIONS.MANUAL_RULE_RUN }); const modalManualRuleRunConfirmationResult = await showManualRuleRunConfirmation(); - telemetry.reportManualRuleRunOpenModal({ + telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunOpenModal, { type: 'single', }); if (modalManualRuleRunConfirmationResult === null) { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.test.tsx index 50c35e7a6e52..a11246b4f52b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.test.tsx @@ -10,11 +10,12 @@ import { render, screen, fireEvent } from '@testing-library/react'; import { ExecutionRunTypeFilter } from '.'; import { RuleRunTypeEnum } from '../../../../../../../common/api/detection_engine/rule_monitoring'; import { useKibana } from '../../../../../../common/lib/kibana'; +import { EventLogEventTypes } from '../../../../../../common/lib/telemetry'; jest.mock('../../../../../../common/lib/kibana'); const mockTelemetry = { - reportEventLogFilterByRunType: jest.fn(), + reportEvent: jest.fn(), }; const mockUseKibana = useKibana as jest.Mock; @@ -28,7 +29,7 @@ mockUseKibana.mockReturnValue({ const items = [RuleRunTypeEnum.backfill, RuleRunTypeEnum.standard]; describe('ExecutionRunTypeFilter', () => { - it('calls telemetry.reportEventLogFilterByRunType on selection change', () => { + it('calls telemetry.reportEvent on selection change', () => { const handleChange = jest.fn(); render(); @@ -40,8 +41,11 @@ describe('ExecutionRunTypeFilter', () => { fireEvent.click(manualRun); expect(handleChange).toHaveBeenCalledWith([RuleRunTypeEnum.backfill]); - expect(mockTelemetry.reportEventLogFilterByRunType).toHaveBeenCalledWith({ - runType: [RuleRunTypeEnum.backfill], - }); + expect(mockTelemetry.reportEvent).toHaveBeenCalledWith( + EventLogEventTypes.EventLogFilterByRunType, + { + runType: [RuleRunTypeEnum.backfill], + } + ); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx index 9f144410a759..4e1c44517c05 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx @@ -15,6 +15,7 @@ import { RULE_EXECUTION_TYPE_STANDARD, } from '../../../../../../common/translations'; import { useKibana } from '../../../../../../common/lib/kibana'; +import { EventLogEventTypes } from '../../../../../../common/lib/telemetry'; interface ExecutionRunTypeFilterProps { items: RuleRunType[]; @@ -42,7 +43,9 @@ const ExecutionRunTypeFilterComponent: React.FC = ( const handleSelectionChange = useCallback( (types: RuleRunType[]) => { onChange(types); - telemetry.reportEventLogFilterByRunType({ runType: types }); + telemetry.reportEvent(EventLogEventTypes.EventLogFilterByRunType, { + runType: types, + }); }, [onChange, telemetry] ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index cf57c9d59b08..5392d730c9e0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -20,6 +20,7 @@ import { useKibana as mockUseKibana } from '../../../common/lib/kibana/__mocks__ import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock'; import { useQueryAlerts } from '../../containers/detection_engine/alerts/use_query'; import { getQuery, groupingSearchResponse } from './grouping_settings/mock'; +import { AlertsEventTypes } from '../../../common/lib/telemetry'; jest.mock('../../containers/detection_engine/alerts/use_query'); jest.mock('../../../sourcerer/containers'); @@ -553,17 +554,23 @@ describe('GroupedAlertsTable', () => { fireEvent.click(getByTestId('group-selector-dropdown')); fireEvent.click(getByTestId('panel-user.name')); - expect(mockedTelemetry.reportAlertsGroupingChanged).toHaveBeenCalledWith({ - groupByField: 'user.name', - tableId: testProps.tableId, - }); + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith( + AlertsEventTypes.AlertsGroupingChanged, + { + groupByField: 'user.name', + tableId: testProps.tableId, + } + ); fireEvent.click(getByTestId('group-selector-dropdown')); fireEvent.click(getByTestId('panel-host.name')); - expect(mockedTelemetry.reportAlertsGroupingChanged).toHaveBeenCalledWith({ - groupByField: 'host.name', - tableId: testProps.tableId, - }); + expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith( + AlertsEventTypes.AlertsGroupingChanged, + { + groupByField: 'host.name', + tableId: testProps.tableId, + } + ); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index a1cbdc800472..c4dd142fc71b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -25,7 +25,7 @@ import type { RunTimeMappings } from '../../../sourcerer/store/model'; import { renderGroupPanel, getStats } from './grouping_settings'; import { useKibana } from '../../../common/lib/kibana'; import { GroupedSubLevel } from './alerts_sub_grouping'; -import { track } from '../../../common/lib/telemetry'; +import { AlertsEventTypes, track } from '../../../common/lib/telemetry'; export interface AlertsTableComponentProps { currentAlertStatusFilterValue?: Status[]; @@ -80,14 +80,18 @@ const GroupedAlertsTableComponent: React.FC = (props) const { onGroupChange, onGroupToggle } = useMemo( () => ({ onGroupChange: ({ groupByField, tableId }: { groupByField: string; tableId: string }) => { - telemetry.reportAlertsGroupingChanged({ groupByField, tableId }); + telemetry.reportEvent(AlertsEventTypes.AlertsGroupingChanged, { groupByField, tableId }); }, onGroupToggle: (param: { isOpen: boolean; groupName?: string | undefined; groupNumber: number; groupingId: string; - }) => telemetry.reportAlertsGroupingToggled({ ...param, tableId: param.groupingId }), + }) => + telemetry.reportEvent(AlertsEventTypes.AlertsGroupingToggled, { + ...param, + tableId: param.groupingId, + }), }), [telemetry] ); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx index 6a7b11ee0e19..e5e753b1c776 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_take_action_items.tsx @@ -28,7 +28,7 @@ import { import { FILTER_ACKNOWLEDGED, FILTER_CLOSED, FILTER_OPEN } from '../../../../../common/types'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import * as i18n from '../translations'; -import { getTelemetryEvent, METRIC_TYPE, track } from '../../../../common/lib/telemetry'; +import { AlertsEventTypes, METRIC_TYPE, track } from '../../../../common/lib/telemetry'; import type { StartServices } from '../../../../types'; export interface TakeActionsProps { @@ -36,6 +36,18 @@ export interface TakeActionsProps { showAlertStatusActions?: boolean; } +const getTelemetryEvent = { + groupedAlertsTakeAction: ({ + tableId, + groupNumber, + status, + }: { + tableId: string; + groupNumber: number; + status: AlertWorkflowStatus; + }) => `alerts_table_${tableId}_group-${groupNumber}_mark-${status}`, +}; + export const useGroupTakeActionsItems = ({ currentStatus, showAlertStatusActions = true, @@ -58,7 +70,7 @@ export const useGroupTakeActionsItems = ({ status: 'open' | 'closed' | 'acknowledged'; groupByField: string; }) => { - telemetry.reportAlertsGroupingTakeAction(params); + telemetry.reportEvent(AlertsEventTypes.AlertsGroupingTakeAction, params); }, [telemetry] ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx index e1ff950bc5e3..408a13fd1a9c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.test.tsx @@ -16,6 +16,7 @@ import { RuleActionsOverflow } from '.'; import { mockRule } from '../../../../detection_engine/rule_management_ui/components/rules_table/__mocks__/mock'; import { TestProviders } from '../../../../common/mock'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; +import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry'; const showBulkDuplicateExceptionsConfirmation = () => Promise.resolve(null); const showManualRuleRunConfirmation = () => Promise.resolve(null); @@ -28,7 +29,7 @@ jest.mock('../../../../detection_engine/rule_management/logic/bulk_actions/use_b jest.mock('../../../../detection_engine/rule_gaps/logic/use_schedule_rule_run'); jest.mock('../../../../common/lib/apm/use_start_transaction'); jest.mock('../../../../common/hooks/use_app_toasts'); -const mockReportManualRuleRunOpenModal = jest.fn(); +const mockReportEvent = jest.fn(); jest.mock('../../../../common/lib/kibana', () => { const actual = jest.requireActual('../../../../common/lib/kibana'); return { @@ -36,8 +37,8 @@ jest.mock('../../../../common/lib/kibana', () => { useKibana: jest.fn().mockReturnValue({ services: { telemetry: { - reportManualRuleRunOpenModal: (params: { type: 'single' | 'bulk' }) => - mockReportManualRuleRunOpenModal(params), + reportEvent: (eventType: ManualRuleRunEventTypes, params: { type: 'single' | 'bulk' }) => + mockReportEvent(eventType, params), }, application: { navigateToApp: jest.fn(), @@ -274,7 +275,7 @@ describe('RuleActionsOverflow', () => { expect(getByTestId('rules-details-popover')).not.toHaveTextContent(/.+/); }); - test('it calls telemetry.reportManualRuleRunOpenModal when rules-details-manual-rule-run is clicked', async () => { + test('it calls telemetry.reportEvent when rules-details-manual-rule-run is clicked', async () => { const { getByTestId } = render( { fireEvent.click(getByTestId('rules-details-manual-rule-run')); await waitFor(() => { - expect(mockReportManualRuleRunOpenModal).toHaveBeenCalledWith({ - type: 'single', - }); + expect(mockReportEvent).toHaveBeenCalledWith( + ManualRuleRunEventTypes.ManualRuleRunOpenModal, + { + type: 'single', + } + ); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx index 68defd759938..a786b95979d4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/index.tsx @@ -34,6 +34,7 @@ import { import { useDownloadExportedRules } from '../../../../detection_engine/rule_management/logic/bulk_actions/use_download_exported_rules'; import * as i18nActions from '../../../pages/detection_engine/rules/translations'; import * as i18n from './translations'; +import { ManualRuleRunEventTypes } from '../../../../common/lib/telemetry'; const MyEuiButtonIcon = styled(EuiButtonIcon)` &.euiButtonIcon { @@ -161,7 +162,7 @@ const RuleActionsOverflowComponent = ({ startTransaction({ name: SINGLE_RULE_ACTIONS.MANUAL_RULE_RUN }); closePopover(); const modalManualRuleRunConfirmationResult = await showManualRuleRunConfirmation(); - telemetry.reportManualRuleRunOpenModal({ + telemetry.reportEvent(ManualRuleRunEventTypes.ManualRuleRunOpenModal, { type: 'single', }); if (modalManualRuleRunConfirmationResult === null) { diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx index dbd47281c5f2..484162cbf7d4 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx @@ -20,7 +20,7 @@ import { useSourcererDataView } from '../../../sourcerer/containers'; import { SourcererScopeName } from '../../../sourcerer/store/model'; import { updateGroups } from '../../../common/store/grouping/actions'; import { useKibana } from '../../../common/lib/kibana'; -import { METRIC_TYPE, track } from '../../../common/lib/telemetry'; +import { METRIC_TYPE, AlertsEventTypes, track } from '../../../common/lib/telemetry'; import { useDataTableFilters } from '../../../common/hooks/use_data_table_filters'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { RightTopMenu } from '../../../common/components/events_viewer/right_top_menu'; @@ -47,7 +47,10 @@ export const getPersistentControlsHook = (tableId: TableId) => { METRIC_TYPE.CLICK, getTelemetryEvent.groupChanged({ groupingId: tableId, selected: groupSelection }) ); - telemetry.reportAlertsGroupingChanged({ groupByField: groupSelection, tableId }); + telemetry.reportEvent(AlertsEventTypes.AlertsGroupingChanged, { + groupByField: groupSelection, + tableId, + }); }, [telemetry] ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/asset_criticality_file_uploader.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/asset_criticality_file_uploader.tsx index 0acd1f831ca7..9c76c1e5f508 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/asset_criticality_file_uploader.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/asset_criticality_file_uploader.tsx @@ -16,6 +16,7 @@ import { AssetCriticalityResultStep } from './components/result_step'; import { useEntityAnalyticsRoutes } from '../../api/api'; import { useFileValidation, useNavigationSteps } from './hooks'; import type { OnCompleteParams } from './types'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export const AssetCriticalityFileUploader: React.FC = () => { const [state, dispatch] = useReducer(reducer, INITIAL_STATE); @@ -24,7 +25,7 @@ export const AssetCriticalityFileUploader: React.FC = () => { const onValidationComplete = useCallback( ({ validatedFile, processingStartTime, processingEndTime, tookMs }: OnCompleteParams) => { - telemetry.reportAssetCriticalityCsvPreviewGenerated({ + telemetry.reportEvent(EntityEventTypes.AssetCriticalityCsvPreviewGenerated, { file: { size: validatedFile.size, }, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.test.tsx index ca1dcfb6e7f4..f3200b5c6ee4 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../../common/lib/kibana/kibana_react', () => ({ useKibana: () => ({ services: { telemetry: { - reportAssetCriticalityCsvImported: jest.fn(), + reportEvent: jest.fn(), }, }, }), diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.tsx index 538e19f4d5fd..c4dadc756c15 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/validation_step.tsx @@ -23,6 +23,7 @@ import { downloadBlob } from '../../../../common/utils/download_blob'; import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import type { ValidatedFile } from '../types'; import { buildAnnotationsFromError } from '../helpers'; +import { EntityEventTypes } from '../../../../common/lib/telemetry'; export interface AssetCriticalityValidationStepProps { validatedFile: ValidatedFile; @@ -42,7 +43,7 @@ export const AssetCriticalityValidationStep: React.FC { - telemetry.reportAssetCriticalityCsvImported({ + telemetry.reportEvent(EntityEventTypes.AssetCriticalityCsvImported, { file: { size: fileSize, }, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/hooks.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/hooks.ts index 107ba6348ac7..0472f5002fcb 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/hooks.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/hooks.ts @@ -17,6 +17,7 @@ import { useKibana } from '../../../common/lib/kibana'; import type { OnCompleteParams } from './types'; import type { ReducerState } from './reducer'; import { getStepStatus, isValidationStep } from './helpers'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; interface UseFileChangeCbParams { onError: (errorMessage: string, file: File) => void; @@ -35,7 +36,7 @@ export const useFileValidation = ({ onError, onComplete }: UseFileChangeCbParams }, file: File ) => { - telemetry.reportAssetCriticalityFileSelected({ + telemetry.reportEvent(EntityEventTypes.AssetCriticalityFileSelected, { valid: false, errorCode: error.code, file: { @@ -62,7 +63,7 @@ export const useFileValidation = ({ onError, onComplete }: UseFileChangeCbParams return; } - telemetry.reportAssetCriticalityFileSelected({ + telemetry.reportEvent(EntityEventTypes.AssetCriticalityFileSelected, { valid: true, file: { size: file.size, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx index 8400578b85c4..32234547ca62 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.test.tsx @@ -10,6 +10,7 @@ import { AnomalyEntity } from '../../../common/components/ml/anomaly/use_anomali import { createTelemetryServiceMock } from '../../../common/lib/telemetry/telemetry_service.mock'; import { TestProviders } from '../../../common/mock'; import { AnomaliesCountLink } from './anomalies_count_link'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; const mockedTelemetry = createTelemetryServiceMock(); jest.mock('../../../common/lib/kibana', () => { @@ -37,6 +38,9 @@ describe('AnomaliesCountLink', () => { fireEvent.click(getByRole('button')); - expect(mockedTelemetry.reportAnomaliesCountClicked).toHaveBeenLastCalledWith({ jobId, count }); + expect(mockedTelemetry.reportEvent).toHaveBeenLastCalledWith( + EntityEventTypes.AnomaliesCountClicked, + { jobId, count } + ); }); }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx index bb32564acb1b..6068d0ece667 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_anomalies/anomalies_count_link.tsx @@ -15,6 +15,7 @@ import { HostsType } from '../../../explore/hosts/store/model'; import { UsersType } from '../../../explore/users/store/model'; import { useKibana } from '../../../common/lib/kibana'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export const AnomaliesCountLink = ({ count, @@ -36,7 +37,7 @@ export const AnomaliesCountLink = ({ const onClick = useCallback(() => { if (!jobId) return; - telemetry.reportAnomaliesCountClicked({ + telemetry.reportEvent(EntityEventTypes.AnomaliesCountClicked, { jobId, count, }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx index ffa1afffdf21..08ce79cc74c1 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx @@ -38,6 +38,7 @@ import { useRiskScore } from '../../api/hooks/use_risk_score'; import { UserPanelKey } from '../../../flyout/entity_details/user_right'; import { RiskEnginePrivilegesCallOut } from '../risk_engine_privileges_callout'; import { useMissingRiskEnginePrivileges } from '../../hooks/use_missing_risk_engine_privileges'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export const ENTITY_RISK_SCORE_TABLE_ID = 'entity-risk-score-table'; @@ -51,7 +52,7 @@ const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskSc const openEntityOnAlertsPage = useCallback( (entityName: string) => { - telemetry.reportEntityAlertsClicked({ entity: riskEntity }); + telemetry.reportEvent(EntityEventTypes.EntityAlertsClicked, { entity: riskEntity }); openAlertsPageWithFilters([ { title: getRiskEntityTranslation(riskEntity), diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts index b12f82128c82..a8a7f60e075a 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts @@ -17,6 +17,7 @@ import { SourcererScopeName } from '../../../../sourcerer/store/model'; import { useAddBulkToTimelineAction } from '../../../../detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline'; import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts'; +import { EntityEventTypes } from '../../../../common/lib/telemetry'; /** * The returned actions only support alerts risk inputs. @@ -61,7 +62,7 @@ export const useRiskInputActions = (inputs: InputAlert[], closePopover: () => vo }, addToNewTimeline: () => { - telemetry.reportAddRiskInputToTimelineClicked({ + telemetry.reportEvent(EntityEventTypes.AddRiskInputToTimelineClicked, { quantity: inputs.length, }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts index 21e73241451e..8aefbe2b44af 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_store/hooks/use_entity_store.ts @@ -17,6 +17,7 @@ import type { } from '../../../../../common/api/entity_analytics'; import { useEntityStoreRoutes } from '../../../api/entity_store'; import { ENTITY_STORE_ENGINE_STATUS, useEntityEngineStatus } from './use_entity_engine_status'; +import { EntityEventTypes } from '../../../../common/lib/telemetry'; const ENTITY_STORE_ENABLEMENT_INIT = 'ENTITY_STORE_ENABLEMENT_INIT'; @@ -49,7 +50,7 @@ export const useEntityStoreEnablement = () => { }); const enable = useCallback(() => { - telemetry?.reportEntityStoreInit({ + telemetry?.reportEvent(EntityEventTypes.EntityStoreDashboardInitButtonClicked, { timestamp: new Date().toISOString(), }); return initialize().then(() => setPolling(true)); @@ -76,7 +77,7 @@ export const useInitEntityEngineMutation = (options?: UseMutationOptions<{}>) => const { initEntityStore } = useEntityStoreRoutes(); return useMutation( () => { - telemetry?.reportEntityStoreEnablement({ + telemetry?.reportEvent(EntityEventTypes.EntityStoreEnablementToggleClicked, { timestamp: new Date().toISOString(), action: 'start', }); @@ -106,7 +107,7 @@ export const useStopEntityEngineMutation = (options?: UseMutationOptions<{}>) => const { stopEntityStore } = useEntityStoreRoutes(); return useMutation( () => { - telemetry?.reportEntityStoreEnablement({ + telemetry?.reportEvent(EntityEventTypes.EntityStoreEnablementToggleClicked, { timestamp: new Date().toISOString(), action: 'stop', }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx index 2dec7d07ce6e..0c42543a7f91 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary_flyout/risk_summary.tsx @@ -43,6 +43,7 @@ import { LENS_VISUALIZATION_MIN_WIDTH, SUMMARY_TABLE_MIN_WIDTH, } from './common'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface RiskSummaryProps { riskScoreData: RiskScoreState; @@ -84,7 +85,7 @@ const FlyoutRiskSummaryComponent = ({ (isOpen: boolean) => { const entity = isUserRiskData(riskData) ? 'user' : 'host'; - telemetry.reportToggleRiskSummaryClicked({ + telemetry.reportEvent(EntityEventTypes.ToggleRiskSummaryClicked, { entity, action: isOpen ? 'show' : 'hide', }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.test.tsx index 8adbc2c7578d..c50d5a1be774 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.test.tsx @@ -24,7 +24,7 @@ jest.mock('../../../common/lib/kibana', () => { describe('SeverityFilter', () => { beforeEach(() => { - mockedTelemetry.reportEntityRiskFiltered.mockClear(); + mockedTelemetry.reportEvent.mockClear(); }); it('sends telemetry when selecting a classification', () => { @@ -38,7 +38,7 @@ describe('SeverityFilter', () => { fireEvent.click(getByTestId('risk-filter-item-Unknown')); - expect(mockedTelemetry.reportEntityRiskFiltered).toHaveBeenCalledTimes(1); + expect(mockedTelemetry.reportEvent).toHaveBeenCalledTimes(1); }); it('does not send telemetry when deselecting a classification', () => { @@ -61,6 +61,6 @@ describe('SeverityFilter', () => { fireEvent.click(getByTestId('risk-filter-popoverButton')); fireEvent.click(getByTestId('risk-filter-item-Unknown')); - expect(mockedTelemetry.reportEntityRiskFiltered).toHaveBeenCalledTimes(0); + expect(mockedTelemetry.reportEvent).toHaveBeenCalledTimes(0); }); }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.tsx index 6aa150e40afa..6da522658894 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter.tsx @@ -13,6 +13,7 @@ import type { RiskScoreEntity, RiskSeverity } from '../../../../common/search_st import { RiskScoreLevel } from './common'; import { ENTITY_RISK_LEVEL } from '../risk_score/translations'; import { useKibana } from '../../../common/lib/kibana'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface SeverityFilterProps { riskEntity?: RiskScoreEntity; @@ -35,7 +36,7 @@ export const SeverityFilter: React.FC = ({ >( (newSelection, changedSeverity, changedStatus) => { if (changedStatus === 'on') { - telemetry.reportEntityRiskFiltered({ + telemetry.reportEvent(EntityEventTypes.EntityRiskFiltered, { entity: riskEntity, selectedSeverity: changedSeverity, }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx index c315e991d9f0..e3fd2fef8bc6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.tsx @@ -71,6 +71,7 @@ import type { NarrowDateRange } from '../../../../common/components/ml/types'; import { MisconfigurationsInsight } from '../../shared/components/misconfiguration_insight'; import { VulnerabilitiesInsight } from '../../shared/components/vulnerabilities_insight'; import { AlertCountInsight } from '../../shared/components/alert_count_insight'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; const HOST_DETAILS_ID = 'entities-hosts-details'; const RELATED_USERS_ID = 'entities-hosts-related-users'; @@ -134,7 +135,7 @@ export const HostDetails: React.FC = ({ hostName, timestamp, s banner: HOST_PREVIEW_BANNER, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.tsx index 38bf50a679ee..3b45cd71b0a6 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.tsx @@ -28,6 +28,7 @@ import { ALERT_PREVIEW_BANNER } from '../../preview/constants'; import { useLicense } from '../../../../common/hooks/use_license'; import { useSessionPreview } from '../../right/hooks/use_session_preview'; import { SessionViewNoDataMessage } from '../../shared/components/session_view_no_data_message'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; export const SESSION_VIEW_ID = 'session-view'; @@ -74,7 +75,7 @@ export const SessionView: FC = () => { isPreviewMode: true, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx index 2f98c641b595..e88cbb54f947 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.tsx @@ -69,6 +69,7 @@ import { PreviewLink } from '../../../shared/components/preview_link'; import type { NarrowDateRange } from '../../../../common/components/ml/types'; import { MisconfigurationsInsight } from '../../shared/components/misconfiguration_insight'; import { AlertCountInsight } from '../../shared/components/alert_count_insight'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; const USER_DETAILS_ID = 'entities-users-details'; const RELATED_HOSTS_ID = 'entities-users-related-hosts'; @@ -133,7 +134,7 @@ export const UserDetails: React.FC = ({ userName, timestamp, s banner: USER_PREVIEW_BANNER, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx index 56375426c5f6..6dcf9da06d2b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/index.tsx @@ -22,6 +22,7 @@ import { getField } from '../shared/utils'; import { EventKind } from '../shared/constants/event_kinds'; import { useDocumentDetailsContext } from '../shared/context'; import type { DocumentDetailsProps } from '../shared/types'; +import { DocumentEventTypes } from '../../../common/lib/telemetry/types'; export type LeftPanelPaths = 'visualize' | 'insights' | 'investigation' | 'response' | 'notes'; export const LeftPanelVisualizeTab: LeftPanelPaths = 'visualize'; @@ -75,7 +76,7 @@ export const LeftPanel: FC> = memo(({ path }) => { scopeId, }, }); - telemetry.reportDetailsFlyoutTabClicked({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, { location: scopeId, panel: 'left', tabId, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx index 3917de03e2e3..0982e10485ba 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/tabs/insights_tab.tsx @@ -31,6 +31,7 @@ import { PREVALENCE_TAB_ID, PrevalenceDetails } from '../components/prevalence_d import { CORRELATIONS_TAB_ID, CorrelationsDetails } from '../components/correlations_details'; import { getField } from '../../shared/utils'; import { EventKind } from '../../shared/constants/event_kinds'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; const ENTITIES_TAB_ID = 'entity'; @@ -113,7 +114,7 @@ export const InsightsTab = memo(() => { scopeId, }, }); - telemetry.reportDetailsFlyoutTabClicked({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, { location: scopeId, panel: 'left', tabId: optionId, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx index 020133288867..b2df6c096e27 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/preview/footer.tsx @@ -16,6 +16,7 @@ import { DocumentDetailsRightPanelKey } from '../shared/constants/panel_keys'; import { useDocumentDetailsContext } from '../shared/context'; import { PREVIEW_FOOTER_TEST_ID, PREVIEW_FOOTER_LINK_TEST_ID } from './test_ids'; import { useKibana } from '../../../common/lib/kibana'; +import { DocumentEventTypes } from '../../../common/lib/telemetry'; /** * Footer at the bottom of preview panel with a link to open document details flyout @@ -41,7 +42,7 @@ export const PreviewPanelFooter = () => { }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_description.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_description.tsx index 2d2dfddbbbab..91d5059e5d60 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_description.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_description.tsx @@ -22,6 +22,7 @@ import { RULE_SUMMARY_BUTTON_TEST_ID, } from './test_ids'; import { RULE_PREVIEW_BANNER, RulePreviewPanelKey } from '../../../rule_details/right'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; /** * Displays the rule description of a signal document. @@ -42,7 +43,7 @@ export const AlertDescription: FC = () => { isPreviewMode: true, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx index c4b0e6e26a82..fc6db84ad439 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/reason.tsx @@ -22,6 +22,7 @@ import { } from './test_ids'; import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data'; import { useDocumentDetailsContext } from '../../shared/context'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; export const ALERT_REASON_BANNER = { title: i18n.translate( @@ -55,7 +56,7 @@ export const Reason: FC = () => { banner: ALERT_REASON_BANNER, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx index 56c24d956209..9d3262ce1ff3 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx @@ -20,6 +20,7 @@ import { PanelContent } from './content'; import type { RightPanelTabType } from './tabs'; import { PanelFooter } from './footer'; import { useFlyoutIsExpandable } from './hooks/use_flyout_is_expandable'; +import { DocumentEventTypes } from '../../../common/lib/telemetry'; export type RightPanelPaths = 'overview' | 'table' | 'json'; @@ -53,7 +54,7 @@ export const RightPanel: FC> = memo(({ path }) => // saving which tab is currently selected in the right panel in local storage storage.set(FLYOUT_STORAGE_KEYS.RIGHT_PANEL_SELECTED_TABS, tabId); - telemetry.reportDetailsFlyoutTabClicked({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, { location: scopeId, panel: 'right', tabId, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/navigation.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/navigation.tsx index b4f12fbabf94..c3ee6a7d7a51 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/navigation.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/navigation.tsx @@ -13,6 +13,7 @@ import { HeaderActions } from './components/header_actions'; import { FlyoutNavigation } from '../../shared/components/flyout_navigation'; import { DocumentDetailsLeftPanelKey } from '../shared/constants/panel_keys'; import { useDocumentDetailsContext } from '../shared/context'; +import { DocumentEventTypes } from '../../../common/lib/telemetry'; interface PanelNavigationProps { /** @@ -35,7 +36,7 @@ export const PanelNavigation: FC = memo(({ flyoutIsExpanda scopeId, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_analyzer.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_analyzer.tsx index 516a43332d29..a4539ed7e641 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_analyzer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_analyzer.tsx @@ -19,6 +19,7 @@ import { } from '../constants/panel_keys'; import { Flyouts } from '../constants/flyouts'; import { isTimelineScope } from '../../../../helpers'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; export interface UseNavigateToAnalyzerParams { /** @@ -107,7 +108,7 @@ export const useNavigateToAnalyzer = ({ if (isFlyoutOpen) { openLeftPanel(left); openPreviewPanel(preview); - telemetry.reportDetailsFlyoutTabClicked({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, { location: scopeId, panel: 'left', tabId: 'visualize', @@ -118,7 +119,7 @@ export const useNavigateToAnalyzer = ({ left, preview, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_session_view.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_session_view.tsx index b8234321217e..f0b2733998c9 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_session_view.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_navigate_to_session_view.tsx @@ -12,6 +12,7 @@ import type { Maybe } from '@kbn/timelines-plugin/common/search_strategy/common' import { useKibana } from '../../../../common/lib/kibana'; import { SESSION_VIEW_ID } from '../../left/components/session_view'; import { DocumentDetailsLeftPanelKey, DocumentDetailsRightPanelKey } from '../constants/panel_keys'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; export interface UseNavigateToSessionViewParams { /** @@ -83,7 +84,7 @@ export const useNavigateToSessionView = ({ const navigateToSessionView = useCallback(() => { if (isFlyoutOpen) { openLeftPanel(left); - telemetry.reportDetailsFlyoutTabClicked({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutTabClicked, { location: scopeId, panel: 'left', tabId: 'visualize', @@ -93,7 +94,7 @@ export const useNavigateToSessionView = ({ right, left, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx index adc54b58f75c..83fa75474a1c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx @@ -35,6 +35,7 @@ import { useObservedHost } from './hooks/use_observed_host'; import { HostDetailsPanelKey } from '../host_details_left'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { HostPreviewPanelFooter } from '../host_preview/footer'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface HostPanelProps extends Record { contextID: string; @@ -130,7 +131,7 @@ export const HostPanel = ({ const openTabPanel = useCallback( (tab?: EntityDetailsLeftPanelTab) => { - telemetry.reportRiskInputsExpandedFlyoutOpened({ + telemetry.reportEvent(EntityEventTypes.RiskInputsExpandedFlyoutOpened, { entity: 'host', }); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index 3a60c06e3fae..42c8664b2ac0 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -33,6 +33,7 @@ import { UserDetailsPanelKey } from '../user_details_left'; import { useObservedUser } from './hooks/use_observed_user'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { UserPreviewPanelFooter } from '../user_preview/footer'; +import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface UserPanelProps extends Record { contextID: string; @@ -123,7 +124,7 @@ export const UserPanel = ({ const { openLeftPanel } = useExpandableFlyoutApi(); const openPanelTab = useCallback( (tab?: EntityDetailsLeftPanelTab) => { - telemetry.reportRiskInputsExpandedFlyoutOpened({ + telemetry.reportEvent(EntityEventTypes.RiskInputsExpandedFlyoutOpened, { entity: 'user', }); diff --git a/x-pack/plugins/security_solution/public/flyout/shared/components/preview_link.tsx b/x-pack/plugins/security_solution/public/flyout/shared/components/preview_link.tsx index fc51a4e64e6c..b6a4ea33ba4b 100644 --- a/x-pack/plugins/security_solution/public/flyout/shared/components/preview_link.tsx +++ b/x-pack/plugins/security_solution/public/flyout/shared/components/preview_link.tsx @@ -24,6 +24,7 @@ import { UserPreviewPanelKey } from '../../entity_details/user_right'; import { USER_PREVIEW_BANNER } from '../../document_details/right/components/user_entity_overview'; import { NetworkPanelKey, NETWORK_PREVIEW_BANNER } from '../../network_details'; import { RulePreviewPanelKey, RULE_PREVIEW_BANNER } from '../../rule_details/right'; +import { DocumentEventTypes } from '../../../common/lib/telemetry'; const PREVIEW_FIELDS = [HOST_NAME_FIELD_NAME, USER_NAME_FIELD_NAME, SIGNAL_RULE_NAME_FIELD_NAME]; @@ -133,7 +134,7 @@ export const PreviewLink: FC = ({ id: previewParams.id, params: previewParams.params, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'preview', }); diff --git a/x-pack/plugins/security_solution/public/notes/components/add_note.tsx b/x-pack/plugins/security_solution/public/notes/components/add_note.tsx index 78a84064467f..5d1ef2ce4d8e 100644 --- a/x-pack/plugins/security_solution/public/notes/components/add_note.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/add_note.tsx @@ -28,6 +28,7 @@ import { userClosedCreateErrorToast, } from '../store/notes.slice'; import { MarkdownEditor } from '../../common/components/markdown_editor'; +import { NotesEventTypes } from '../../common/lib/telemetry'; export const MARKDOWN_ARIA_LABEL = i18n.translate( 'xpack.securitySolution.notes.addNote.markdownAriaLabel', @@ -96,7 +97,7 @@ export const AddNote = memo( if (onNoteAdd) { onNoteAdd(); } - telemetry.reportAddNoteFromExpandableFlyoutClicked({ + telemetry.reportEvent(NotesEventTypes.AddNoteFromExpandableFlyoutClicked, { isRelatedToATimeline: timelineId != null, }); setEditorValue(''); diff --git a/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx b/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx index 85e9e24c6f26..65e6389fc2fd 100644 --- a/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/open_flyout_button.tsx @@ -16,6 +16,7 @@ import { useSourcererDataView } from '../../sourcerer/containers'; import { SourcererScopeName } from '../../sourcerer/store/model'; import { useKibana } from '../../common/lib/kibana'; import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/shared/constants/panel_keys'; +import { DocumentEventTypes } from '../../common/lib/telemetry'; export const OPEN_FLYOUT_BUTTON = i18n.translate( 'xpack.securitySolution.notes.openFlyoutButtonLabel', @@ -61,7 +62,7 @@ export const OpenFlyoutButtonIcon = memo( }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_context.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_context.tsx index dda17e18c087..2a6597628a26 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_context.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_context.tsx @@ -9,6 +9,7 @@ import type { PropsWithChildren } from 'react'; import React, { createContext, useContext, useMemo } from 'react'; import { useKibana } from '../../common/lib/kibana/kibana_react'; import type { OnboardingCardId } from '../constants'; +import { OnboardingHubEventTypes } from '../../common/lib/telemetry'; export interface OnboardingContextValue { spaceId: string; @@ -26,19 +27,19 @@ export const OnboardingContextProvider: React.FC ({ spaceId, reportCardOpen: (cardId, { auto = false } = {}) => { - telemetry.reportOnboardingHubStepOpen({ + telemetry.reportEvent(OnboardingHubEventTypes.OnboardingHubStepOpen, { stepId: cardId, trigger: auto ? 'navigation' : 'click', }); }, reportCardComplete: (cardId, { auto = false } = {}) => { - telemetry.reportOnboardingHubStepFinished({ + telemetry.reportEvent(OnboardingHubEventTypes.OnboardingHubStepFinished, { stepId: cardId, trigger: auto ? 'auto_check' : 'click', }); }, reportCardLinkClicked: (cardId, linkId: string) => { - telemetry.reportOnboardingHubStepLinkClicked({ + telemetry.reportEvent(OnboardingHubEventTypes.OnboardingHubStepLinkClicked, { originStepId: cardId, stepLinkId: linkId, }); diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx index 67dcc3848f02..e785e5843543 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx @@ -28,9 +28,10 @@ import { KibanaServices, useKibana, useToasts, useUiSetting$ } from '../../commo import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useSignalIndex } from '../../detections/containers/detection_engine/alerts/use_signal_index'; import * as i18n from './translations'; -import type { - ReportDataQualityCheckAllCompletedParams, - ReportDataQualityIndexCheckedParams, +import { + type ReportDataQualityCheckAllCompletedParams, + type ReportDataQualityIndexCheckedParams, + DataQualityEventTypes, } from '../../common/lib/telemetry'; const LOCAL_STORAGE_KEY = 'dataQualityDashboardLastChecked'; @@ -118,14 +119,14 @@ const DataQualityComponent: React.FC = () => { const reportDataQualityIndexChecked = useCallback( (params: ReportDataQualityIndexCheckedParams) => { - telemetry.reportDataQualityIndexChecked(params); + telemetry.reportEvent(DataQualityEventTypes.DataQualityIndexChecked, params); }, [telemetry] ); const reportDataQualityCheckAllCompleted = useCallback( (params: ReportDataQualityCheckAllCompletedParams) => { - telemetry.reportDataQualityCheckAllCompleted(params); + telemetry.reportEvent(DataQualityEventTypes.DataQualityCheckAllCompleted, params); }, [telemetry] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx index fe53c36d3f04..19f98aff651f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx @@ -34,6 +34,7 @@ import { SourcererScopeName } from '../../../../sourcerer/store/model'; import { useSourcererDataView } from '../../../../sourcerer/containers'; import { useDeleteNote } from './hooks/use_delete_note'; import { getTimelineNoteSelector } from '../../timeline/tabs/notes/selectors'; +import { DocumentEventTypes } from '../../../../common/lib/telemetry'; export const NotePreviewsContainer = styled.section` padding-top: ${({ theme }) => `${theme.eui.euiSizeS}`}; @@ -66,7 +67,7 @@ const ToggleEventDetailsButtonComponent: React.FC }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx index 602d2353f342..5c4a592d99a7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/eql/index.tsx @@ -46,6 +46,7 @@ import { useTimelineControlColumn } from '../shared/use_timeline_control_columns import { LeftPanelNotesTab } from '../../../../../flyout/document_details/left'; import { useNotesInFlyout } from '../../properties/use_notes_in_flyout'; import { NotesFlyout } from '../../properties/notes_flyout'; +import { NotesEventTypes, DocumentEventTypes } from '../../../../../common/lib/telemetry'; import { TimelineRefetch } from '../../refetch_timeline'; export type Props = TimelineTabCommonProps & PropsFromRedux; @@ -161,10 +162,10 @@ export const EqlTabContentComponent: React.FC = ({ }, }, }); - telemetry.reportOpenNoteInExpandableFlyoutClicked({ + telemetry.reportEvent(NotesEventTypes.OpenNoteInExpandableFlyoutClicked, { location: timelineId, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx index 1f2360daa051..0b2553d23ac5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/pinned/index.tsx @@ -36,6 +36,7 @@ import { useTimelineControlColumn } from '../shared/use_timeline_control_columns import { LeftPanelNotesTab } from '../../../../../flyout/document_details/left'; import { useNotesInFlyout } from '../../properties/use_notes_in_flyout'; import { NotesFlyout } from '../../properties/notes_flyout'; +import { NotesEventTypes, DocumentEventTypes } from '../../../../../common/lib/telemetry'; import { defaultUdtHeaders } from '../../body/column_headers/default_headers'; interface PinnedFilter { @@ -190,10 +191,10 @@ export const PinnedTabContentComponent: React.FC = ({ }, }, }); - telemetry.reportOpenNoteInExpandableFlyoutClicked({ + telemetry.reportEvent(NotesEventTypes.OpenNoteInExpandableFlyoutClicked, { location: timelineId, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx index 8ea1db39a361..ec61c67a3954 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/query/index.tsx @@ -49,6 +49,7 @@ import { useTimelineColumns } from '../shared/use_timeline_columns'; import { useTimelineControlColumn } from '../shared/use_timeline_control_columns'; import { NotesFlyout } from '../../properties/notes_flyout'; import { useNotesInFlyout } from '../../properties/use_notes_in_flyout'; +import { DocumentEventTypes, NotesEventTypes } from '../../../../../common/lib/telemetry'; const compareQueryProps = (prevProps: Props, nextProps: Props) => prevProps.kqlMode === nextProps.kqlMode && @@ -228,10 +229,10 @@ export const QueryTabContentComponent: React.FC = ({ }, }, }); - telemetry.reportOpenNoteInExpandableFlyoutClicked({ + telemetry.reportEvent(NotesEventTypes.OpenNoteInExpandableFlyoutClicked, { location: timelineId, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'left', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.tsx index c711208cb680..67b9fd50eac2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.tsx @@ -34,6 +34,7 @@ import { useUserPrivileges } from '../../../../../common/components/user_privile import { timelineActions, timelineSelectors } from '../../../../store'; import { timelineDefaults } from '../../../../store/defaults'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; +import { DocumentEventTypes } from '../../../../../common/lib/telemetry'; import { isFullScreen } from '../../helpers'; const FullScreenButtonIcon = styled(EuiButtonIcon)` @@ -287,7 +288,7 @@ export const useSessionView = ({ scopeId, height }: { scopeId: string; height?: }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: scopeId, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx index 875c147d6a70..fa5b83f23576 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx @@ -48,6 +48,7 @@ import { transformTimelineItemToUnifiedRows } from '../utils'; import { TimelineEventDetailRow } from './timeline_event_detail_row'; import { CustomTimelineDataGridBody } from './custom_timeline_data_grid_body'; import { TIMELINE_EVENT_DETAIL_ROW_ID } from '../../body/constants'; +import { DocumentEventTypes } from '../../../../../common/lib/telemetry/types'; export const SAMPLE_SIZE_SETTING = 500; const DataGridMemoized = React.memo(UnifiedDataTable); @@ -165,7 +166,7 @@ export const TimelineDataTableComponent: React.FC = memo( }, }, }); - telemetry.reportDetailsFlyoutOpened({ + telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, { location: timelineId, panel: 'right', }); diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 6ac8b349b74c..55fce6a46dba 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -84,7 +84,6 @@ import type { Assets } from './assets'; import type { Investigations } from './investigations'; import type { MachineLearning } from './machine_learning'; -import type { TelemetryClientStart } from './common/lib/telemetry'; import type { Dashboards } from './dashboards'; import type { BreadcrumbsNav } from './common/breadcrumbs/types'; import type { TopValuesPopoverService } from './app/components/top_values_popover/top_values_popover_service'; @@ -93,6 +92,7 @@ import type { SetComponents, GetComponents$ } from './contract_components'; import type { ConfigSettings } from '../common/config_settings'; import type { OnboardingService } from './onboarding/service'; import type { SolutionNavigation } from './app/solution_navigation/solution_navigation'; +import type { TelemetryServiceStart } from './common/lib/telemetry'; export interface SetupPlugins { cloud?: CloudSetup; @@ -188,7 +188,7 @@ export type StartServices = CoreStart & getPluginWrapper: () => typeof SecuritySolutionTemplateWrapper; }; contentManagement: ContentManagementPublicStart; - telemetry: TelemetryClientStart; + telemetry: TelemetryServiceStart; customDataService: DataPublicPluginStart; topValuesPopover: TopValuesPopoverService; timelineDataService: DataPublicPluginStart; From e03e59b6d482a05435d86a612d92028f264df893 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Mon, 11 Nov 2024 13:24:32 -0600 Subject: [PATCH 021/100] [ES3][Search] Create Index Page (#199402) ## Summary This PR introduces a Create Index page for the serverless search solution. This page is almost identical to the new Global Empty State, but is navigated to via the Create Index button in Index Management. The index details redirect logic is also slightly different on the Create Index page, it will only redirect when the "code" view is open and a new index is created. instead of redirecting from both UI and Code view like the Global Empty State page does. With the addition of this page we are also removing the "Home" link from the serverless search side nav to reduce confusion when the global empty start redirects to index management when indices exist. There is also some minor clean-up to ensure both the global empty state and the new create index pages have proper document titles and breadcrumbs. ### Screenshots Updates to Global Empty State: ![image](https://github.com/user-attachments/assets/bb60734e-543d-4481-b121-d52633d462a8) Create Index Page: image ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- packages/deeplinks/search/constants.ts | 1 + packages/deeplinks/search/deep_links.ts | 6 +- .../components/no_match/no_match.tsx | 7 +- .../create_index/create_index_button.tsx | 14 +- .../index_list/index_table/index_table.js | 6 +- .../public/analytics/constants.ts | 13 +- .../components/create_index/create_index.tsx | 114 ++++++++ .../create_index/create_index_code_view.tsx | 26 ++ .../create_index/create_index_page.tsx | 60 ++++ .../create_index/create_index_ui_view.tsx | 76 +++++ .../hooks/use_indices_redirect.tsx | 51 ++++ .../components/indices/details_page.tsx | 35 +-- .../{indices => }/indices_router.tsx | 11 +- .../{start => shared}/api_key_callout.tsx | 8 +- .../public/components/shared/breadcrumbs.ts | 24 ++ .../create_index_code_view.tsx} | 58 ++-- .../components/shared/create_index_form.tsx | 165 +++++++++++ .../components/shared/create_index_panel.tsx | 271 ++++++++++++++++++ .../hooks/use_create_index.tsx | 2 +- .../use_create_index_coding_examples.tsx} | 2 +- .../load_indices_status_error.tsx} | 6 +- .../public/components/start/create_index.tsx | 169 ++--------- .../components/start/elasticsearch_start.tsx | 261 ++++------------- .../start/hooks/use_indices_redirect.tsx | 2 +- .../public/components/start/start_page.tsx | 12 +- .../public/components/start/types.ts | 14 - .../components/{start/hooks => }/utils.ts | 7 +- .../public/hooks/use_page_chrome.ts | 37 +++ .../plugins/search_indices/public/locators.ts | 29 ++ .../plugins/search_indices/public/plugin.ts | 26 +- .../plugins/search_indices/public/routes.ts | 1 + x-pack/plugins/search_indices/public/types.ts | 43 ++- .../public/utils/indices.test.ts | 29 +- .../search_indices/public/utils/indices.ts | 9 + x-pack/plugins/search_indices/tsconfig.json | 4 +- .../setup_page/create_index_button.tsx | 4 +- .../public/navigation_tree.ts | 13 +- .../translations/translations/fr-FR.json | 50 ++-- .../translations/translations/ja-JP.json | 50 ++-- .../translations/translations/zh-CN.json | 49 ++-- .../page_objects/index_management_page.ts | 2 +- .../functional/page_objects/index.ts | 2 + .../svl_search_create_index_page.ts | 106 +++++++ .../svl_search_elasticsearch_start_page.ts | 14 + .../index_management/index_detail.ts | 2 +- .../management/index_management/indices.ts | 2 +- .../test_suites/search/elasticsearch_start.ts | 13 + .../functional/test_suites/search/index.ts | 1 + .../test_suites/search/index_management.ts | 81 +++++- .../test_suites/search/navigation.ts | 12 +- .../search_playground/playground_overview.ts | 4 +- 51 files changed, 1420 insertions(+), 584 deletions(-) create mode 100644 x-pack/plugins/search_indices/public/components/create_index/create_index.tsx create mode 100644 x-pack/plugins/search_indices/public/components/create_index/create_index_code_view.tsx create mode 100644 x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx create mode 100644 x-pack/plugins/search_indices/public/components/create_index/create_index_ui_view.tsx create mode 100644 x-pack/plugins/search_indices/public/components/create_index/hooks/use_indices_redirect.tsx rename x-pack/plugins/search_indices/public/components/{indices => }/indices_router.tsx (80%) rename x-pack/plugins/search_indices/public/components/{start => shared}/api_key_callout.tsx (82%) create mode 100644 x-pack/plugins/search_indices/public/components/shared/breadcrumbs.ts rename x-pack/plugins/search_indices/public/components/{start/create_index_code.tsx => shared/create_index_code_view.tsx} (67%) create mode 100644 x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx create mode 100644 x-pack/plugins/search_indices/public/components/shared/create_index_panel.tsx rename x-pack/plugins/search_indices/public/components/{start => shared}/hooks/use_create_index.tsx (94%) rename x-pack/plugins/search_indices/public/components/{start/hooks/use_coding_examples.tsx => shared/hooks/use_create_index_coding_examples.tsx} (87%) rename x-pack/plugins/search_indices/public/components/{start/status_error.tsx => shared/load_indices_status_error.tsx} (79%) delete mode 100644 x-pack/plugins/search_indices/public/components/start/types.ts rename x-pack/plugins/search_indices/public/components/{start/hooks => }/utils.ts (66%) create mode 100644 x-pack/plugins/search_indices/public/hooks/use_page_chrome.ts create mode 100644 x-pack/plugins/search_indices/public/locators.ts create mode 100644 x-pack/test_serverless/functional/page_objects/svl_search_create_index_page.ts diff --git a/packages/deeplinks/search/constants.ts b/packages/deeplinks/search/constants.ts index 9848bb0c3d42..52f7bb201388 100644 --- a/packages/deeplinks/search/constants.ts +++ b/packages/deeplinks/search/constants.ts @@ -25,3 +25,4 @@ export const SEARCH_ELASTICSEARCH = 'enterpriseSearchElasticsearch'; export const SEARCH_VECTOR_SEARCH = 'enterpriseSearchVectorSearch'; export const SEARCH_SEMANTIC_SEARCH = 'enterpriseSearchSemanticSearch'; export const SEARCH_AI_SEARCH = 'enterpriseSearchAISearch'; +export const SEARCH_INDICES_CREATE_INDEX = 'createIndex'; diff --git a/packages/deeplinks/search/deep_links.ts b/packages/deeplinks/search/deep_links.ts index 22dfb91bdff3..b23a86b3cc51 100644 --- a/packages/deeplinks/search/deep_links.ts +++ b/packages/deeplinks/search/deep_links.ts @@ -22,6 +22,7 @@ import { SEARCH_HOMEPAGE, SEARCH_INDICES_START, SEARCH_INDICES, + SEARCH_INDICES_CREATE_INDEX, SEARCH_ELASTICSEARCH, SEARCH_VECTOR_SEARCH, SEARCH_SEMANTIC_SEARCH, @@ -55,6 +56,8 @@ export type AppsearchLinkId = 'engines'; export type RelevanceLinkId = 'inferenceEndpoints'; +export type SearchIndicesLinkId = typeof SEARCH_INDICES_CREATE_INDEX; + export type DeepLinkId = | EnterpriseSearchApp | EnterpriseSearchContentApp @@ -77,4 +80,5 @@ export type DeepLinkId = | SearchElasticsearch | SearchVectorSearch | SearchSemanticSearch - | SearchAISearch; + | SearchAISearch + | `${SearchIndices}:${SearchIndicesLinkId}`; diff --git a/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx b/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx index 7f5b3f4b4b7d..15e306bb396b 100644 --- a/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx +++ b/x-pack/plugins/index_management/public/application/components/no_match/no_match.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; import { CreateIndexButton } from '../../sections/home/index_list/create_index/create_index_button'; import { ExtensionsService } from '../../../services/extensions_service'; @@ -16,11 +17,13 @@ export const NoMatch = ({ filter, resetFilter, extensionsService, + share, }: { loadIndices: () => void; filter: string; resetFilter: () => void; extensionsService: ExtensionsService; + share?: SharePluginStart; }) => { if (filter) { return ( @@ -62,7 +65,7 @@ export const NoMatch = ({ if (extensionsService.emptyListContent) { return extensionsService.emptyListContent.renderContent({ - createIndexButton: , + createIndexButton: , }); } @@ -85,7 +88,7 @@ export const NoMatch = ({ />

} - actions={} + actions={} /> ); }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx index 746d684f48b7..e7201ce5d44b 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/create_index/create_index_button.tsx @@ -7,22 +7,32 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; import { EuiButton } from '@elastic/eui'; import { CreateIndexModal } from './create_index_modal'; -export const CreateIndexButton = ({ loadIndices }: { loadIndices: () => void }) => { +export interface CreateIndexButtonProps { + loadIndices: () => void; + share?: SharePluginStart; +} + +export const CreateIndexButton = ({ loadIndices, share }: CreateIndexButtonProps) => { const [createIndexModalOpen, setCreateIndexModalOpen] = useState(false); + const createIndexUrl = share?.url.locators.get('SEARCH_CREATE_INDEX')?.useUrl({}); + const actionProp = createIndexUrl + ? { href: createIndexUrl } + : { onClick: () => setCreateIndexModalOpen(true) }; return ( <> setCreateIndexModalOpen(true)} key="createIndexButton" data-test-subj="createIndexButton" data-telemetry-id="idxMgmt-indexList-createIndexButton" + {...actionProp} > - {({ services, config, core }) => { + {({ services, config, core, plugins }) => { const { extensionsService } = services; const { application, http } = core; + const { share } = plugins; const columnConfigs = getColumnConfigs({ showIndexStats: config.enableIndexStats, showSizeAndDocCount: config.enableSizeAndDocCount, @@ -669,7 +670,7 @@ export class IndexTable extends Component { )} - +
@@ -714,6 +715,7 @@ export class IndexTable extends Component { filterChanged('')} extensionsService={extensionsService} diff --git a/x-pack/plugins/search_indices/public/analytics/constants.ts b/x-pack/plugins/search_indices/public/analytics/constants.ts index d64019d6ef67..0da7aedf1932 100644 --- a/x-pack/plugins/search_indices/public/analytics/constants.ts +++ b/x-pack/plugins/search_indices/public/analytics/constants.ts @@ -12,9 +12,9 @@ export enum AnalyticsEvents { startCreateIndexPageModifyIndexName = 'start_modify_index_name', startCreateIndexClick = 'start_create_index', startCreateIndexLanguageSelect = 'start_code_lang_select', + startCreateIndexRunInConsole = 'start_cta_run_in_console', startCreateIndexCodeCopyInstall = 'start_code_copy_install', startCreateIndexCodeCopy = 'start_code_copy', - startCreateIndexRunInConsole = 'start_cta_run_in_console', startCreateIndexCreatedRedirect = 'start_index_created_api', startFileUploadClick = 'start_file_upload', indexDetailsInstallCodeCopy = 'index_details_code_copy_install', @@ -23,4 +23,15 @@ export enum AnalyticsEvents { indexDetailsNavDataTab = 'index_details_nav_data_tab', indexDetailsNavSettingsTab = 'index_details_nav_settings_tab', indexDetailsNavMappingsTab = 'index_details_nav_mappings_tab', + createIndexPageOpened = 'create_index_page_opened', + createIndexShowCodeClick = 'create_index_show_code', + createIndexShowUIClick = 'create_index_show_create_index_ui', + createIndexPageModifyIndexName = 'create_index_modify_index_name', + createIndexCreateIndexClick = 'create_index_click_create', + createIndexLanguageSelect = 'create_index_code_lang_select', + createIndexRunInConsole = 'create_index_run_in_console', + createIndexCodeCopyInstall = 'create_index_copy_install', + createIndexCodeCopy = 'create_index_code_copy', + createIndexFileUploadClick = 'create_index_file_upload', + createIndexIndexCreatedRedirect = 'create_index_created_api', } diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx new file mode 100644 index 000000000000..d8ce8073c691 --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useState } from 'react'; + +import type { IndicesStatusResponse, UserStartPrivilegesResponse } from '../../../common'; + +import { AnalyticsEvents } from '../../analytics/constants'; +import { AvailableLanguages } from '../../code_examples'; +import { useKibana } from '../../hooks/use_kibana'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { CreateIndexFormState } from '../../types'; +import { generateRandomIndexName } from '../../utils/indices'; +import { getDefaultCodingLanguage } from '../../utils/language'; + +import { CreateIndexPanel } from '../shared/create_index_panel'; + +import { CreateIndexCodeView } from './create_index_code_view'; +import { CreateIndexUIView } from './create_index_ui_view'; + +function initCreateIndexState() { + const defaultIndexName = generateRandomIndexName(); + return { + indexName: defaultIndexName, + defaultIndexName, + codingLanguage: getDefaultCodingLanguage(), + }; +} + +export interface CreateIndexProps { + indicesData?: IndicesStatusResponse; + userPrivileges?: UserStartPrivilegesResponse; +} + +enum CreateIndexViewMode { + UI = 'ui', + Code = 'code', +} + +export const CreateIndex = ({ indicesData, userPrivileges }: CreateIndexProps) => { + const { application } = useKibana().services; + const [createIndexView, setCreateIndexView] = useState( + userPrivileges?.privileges.canCreateIndex === false + ? CreateIndexViewMode.Code + : CreateIndexViewMode.UI + ); + const [formState, setFormState] = useState(initCreateIndexState); + const usageTracker = useUsageTracker(); + const onChangeView = useCallback( + (id: string) => { + switch (id) { + case CreateIndexViewMode.UI: + usageTracker.click(AnalyticsEvents.createIndexShowUIClick); + setCreateIndexView(CreateIndexViewMode.UI); + return; + case CreateIndexViewMode.Code: + usageTracker.click(AnalyticsEvents.createIndexShowCodeClick); + setCreateIndexView(CreateIndexViewMode.Code); + return; + } + }, + [usageTracker] + ); + const onChangeCodingLanguage = useCallback( + (language: AvailableLanguages) => { + setFormState({ + ...formState, + codingLanguage: language, + }); + usageTracker.count([ + AnalyticsEvents.createIndexLanguageSelect, + `${AnalyticsEvents.createIndexLanguageSelect}_${language}`, + ]); + }, + [usageTracker, formState, setFormState] + ); + const onClose = useCallback(() => { + application.navigateToApp('management', { deepLinkId: 'index_management' }); + }, [application]); + + return ( + + {createIndexView === CreateIndexViewMode.UI && ( + + )} + {createIndexView === CreateIndexViewMode.Code && ( + + )} + + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index_code_view.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index_code_view.tsx new file mode 100644 index 000000000000..cdadfbdc146f --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index_code_view.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import type { IndicesStatusResponse } from '../../../common'; +import { + CreateIndexCodeView as SharedCreateIndexCodeView, + CreateIndexCodeViewProps as SharedCreateIndexCodeViewProps, +} from '../shared/create_index_code_view'; + +import { useIndicesRedirect } from './hooks/use_indices_redirect'; + +export interface CreateIndexCodeViewProps extends SharedCreateIndexCodeViewProps { + indicesData?: IndicesStatusResponse; +} + +export const CreateIndexCodeView = ({ indicesData, ...props }: CreateIndexCodeViewProps) => { + useIndicesRedirect(indicesData); + + return ; +}; diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx new file mode 100644 index 000000000000..d8601e95760d --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiLoadingLogo, EuiPageTemplate } from '@elastic/eui'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; + +import { useKibana } from '../../hooks/use_kibana'; +import { useIndicesStatusQuery } from '../../hooks/api/use_indices_status'; +import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; +import { LoadIndicesStatusError } from '../shared/load_indices_status_error'; + +import { CreateIndex } from './create_index'; +import { usePageChrome } from '../../hooks/use_page_chrome'; +import { IndexManagementBreadcrumbs } from '../shared/breadcrumbs'; + +const CreateIndexLabel = i18n.translate('xpack.searchIndices.createIndex.docTitle', { + defaultMessage: 'Create Index', +}); + +export const CreateIndexPage = () => { + const { console: consolePlugin } = useKibana().services; + const { + data: indicesData, + isInitialLoading, + isError: hasIndicesStatusFetchError, + error: indicesFetchError, + } = useIndicesStatusQuery(); + const { data: userPrivileges } = useUserPrivilegesQuery(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + usePageChrome(CreateIndexLabel, [...IndexManagementBreadcrumbs, { text: CreateIndexLabel }]); + + return ( + + + {isInitialLoading && } + {hasIndicesStatusFetchError && } + {!isInitialLoading && !hasIndicesStatusFetchError && ( + + )} + + {embeddableConsole} + + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index_ui_view.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index_ui_view.tsx new file mode 100644 index 000000000000..08073c0e8479 --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index_ui_view.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useState } from 'react'; + +import type { UserStartPrivilegesResponse } from '../../../common'; +import { AnalyticsEvents } from '../../analytics/constants'; +import { CreateIndexFormState } from '../../types'; +import { CreateIndexForm } from '../shared/create_index_form'; +import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { isValidIndexName } from '../../utils/indices'; +import { useCreateIndex } from '../shared/hooks/use_create_index'; + +import { useKibana } from '../../hooks/use_kibana'; + +export interface CreateIndexUIViewProps { + formState: CreateIndexFormState; + setFormState: (value: CreateIndexFormState) => void; + userPrivileges?: UserStartPrivilegesResponse; +} + +export const CreateIndexUIView = ({ + formState, + setFormState, + userPrivileges, +}: CreateIndexUIViewProps) => { + const [indexNameHasError, setIndexNameHasError] = useState(false); + const { application } = useKibana().services; + const usageTracker = useUsageTracker(); + const { createIndex, isLoading } = useCreateIndex(); + const onIndexNameChange = (e: React.ChangeEvent) => { + const newIndexName = e.target.value; + setFormState({ ...formState, indexName: e.target.value }); + const invalidIndexName = !isValidIndexName(newIndexName); + if (indexNameHasError !== invalidIndexName) { + setIndexNameHasError(invalidIndexName); + } + }; + const onCreateIndex = useCallback( + (e: React.FormEvent) => { + e.preventDefault(); + if (!isValidIndexName(formState.indexName)) { + return; + } + usageTracker.click(AnalyticsEvents.createIndexCreateIndexClick); + + if (formState.defaultIndexName !== formState.indexName) { + usageTracker.click(AnalyticsEvents.createIndexPageModifyIndexName); + } + + createIndex({ indexName: formState.indexName }); + }, + [usageTracker, createIndex, formState.indexName, formState.defaultIndexName] + ); + const onFileUpload = useCallback(() => { + usageTracker.click(AnalyticsEvents.createIndexFileUploadClick); + application.navigateToApp('ml', { path: 'filedatavisualizer' }); + }, [usageTracker, application]); + + return ( + + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/create_index/hooks/use_indices_redirect.tsx b/x-pack/plugins/search_indices/public/components/create_index/hooks/use_indices_redirect.tsx new file mode 100644 index 000000000000..4246976209a9 --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/create_index/hooks/use_indices_redirect.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useState } from 'react'; + +import type { IndicesStatusResponse } from '../../../../common'; + +import { useKibana } from '../../../hooks/use_kibana'; + +import { getFirstNewIndexName } from '../../../utils/indices'; +import { navigateToIndexDetails } from '../../utils'; +import { useUsageTracker } from '../../../contexts/usage_tracker_context'; +import { AnalyticsEvents } from '../../../analytics/constants'; + +export const useIndicesRedirect = (indicesStatus?: IndicesStatusResponse) => { + const { application, http } = useKibana().services; + const [initialStatus, setInitialStatus] = useState(undefined); + const [hasDoneRedirect, setHasDoneRedirect] = useState(() => false); + const usageTracker = useUsageTracker(); + return useEffect(() => { + if (hasDoneRedirect) { + return; + } + if (!indicesStatus) { + return; + } + if (initialStatus === undefined) { + setInitialStatus(indicesStatus); + return; + } + const newIndexName = getFirstNewIndexName(initialStatus.indexNames, indicesStatus.indexNames); + if (newIndexName) { + navigateToIndexDetails(application, http, newIndexName); + setHasDoneRedirect(true); + usageTracker.click(AnalyticsEvents.createIndexIndexCreatedRedirect); + return; + } + }, [ + application, + http, + indicesStatus, + initialStatus, + setHasDoneRedirect, + usageTracker, + hasDoneRedirect, + ]); +}; diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx index ad5e174dd6e4..c672bb51493f 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx @@ -37,20 +37,14 @@ import { SearchIndexDetailsPageMenuItemPopover } from './details_page_menu_item' import { useIndexDocumentSearch } from '../../hooks/api/use_document_search'; import { useUsageTracker } from '../../contexts/usage_tracker_context'; import { AnalyticsEvents } from '../../analytics/constants'; +import { usePageChrome } from '../../hooks/use_page_chrome'; +import { IndexManagementBreadcrumbs } from '../shared/breadcrumbs'; export const SearchIndexDetailsPage = () => { const indexName = decodeURIComponent(useParams<{ indexName: string }>().indexName); const tabId = decodeURIComponent(useParams<{ tabId: string }>().tabId); - const { - console: consolePlugin, - docLinks, - application, - history, - share, - chrome, - serverless, - } = useKibana().services; + const { console: consolePlugin, docLinks, application, history, share } = useKibana().services; const { data: index, refetch, @@ -82,23 +76,12 @@ export const SearchIndexDetailsPage = () => { setHasDocuments(!(!isInitialLoading && indexDocuments?.results?.data.length === 0)); }, [indexDocuments, isInitialLoading, setHasDocuments, setDocumentsLoading]); - useEffect(() => { - chrome.docTitle.change(indexName); - - if (serverless) { - serverless.setBreadcrumbs([ - { - text: i18n.translate('xpack.searchIndices.indexBreadcrumbLabel', { - defaultMessage: 'Index Management', - }), - href: '/app/management/data/index_management/indices', - }, - { - text: indexName, - }, - ]); - } - }, [chrome, indexName, serverless]); + usePageChrome(indexName, [ + ...IndexManagementBreadcrumbs, + { + text: indexName, + }, + ]); const usageTracker = useUsageTracker(); diff --git a/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx b/x-pack/plugins/search_indices/public/components/indices_router.tsx similarity index 80% rename from x-pack/plugins/search_indices/public/components/indices/indices_router.tsx rename to x-pack/plugins/search_indices/public/components/indices_router.tsx index 51527a7d2ef8..56ccb3c0674e 100644 --- a/x-pack/plugins/search_indices/public/components/indices/indices_router.tsx +++ b/x-pack/plugins/search_indices/public/components/indices_router.tsx @@ -7,13 +7,17 @@ import React from 'react'; import { Route, Router, Routes } from '@kbn/shared-ux-router'; import { Redirect } from 'react-router-dom'; -import { useKibana } from '../../hooks/use_kibana'; + +import { useKibana } from '../hooks/use_kibana'; import { SearchIndexDetailsTabs, SEARCH_INDICES_DETAILS_PATH, SEARCH_INDICES_DETAILS_TABS_PATH, -} from '../../routes'; -import { SearchIndexDetailsPage } from './details_page'; + CREATE_INDEX_PATH, +} from '../routes'; +import { SearchIndexDetailsPage } from './indices/details_page'; +import { CreateIndexPage } from './create_index/create_index_page'; + export const SearchIndicesRouter: React.FC = () => { const { application, history } = useKibana().services; return ( @@ -29,6 +33,7 @@ export const SearchIndicesRouter: React.FC = () => { /> + { application.navigateToApp('elasticsearchStart'); diff --git a/x-pack/plugins/search_indices/public/components/start/api_key_callout.tsx b/x-pack/plugins/search_indices/public/components/shared/api_key_callout.tsx similarity index 82% rename from x-pack/plugins/search_indices/public/components/start/api_key_callout.tsx rename to x-pack/plugins/search_indices/public/components/shared/api_key_callout.tsx index 65363e9f7322..1fe6c6d1a7ed 100644 --- a/x-pack/plugins/search_indices/public/components/start/api_key_callout.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/api_key_callout.tsx @@ -17,19 +17,19 @@ interface APIKeyCalloutProps { export const APIKeyCallout = ({ apiKey }: APIKeyCalloutProps) => { const title = apiKey - ? i18n.translate('xpack.searchIndices.startPage.codeView.apiKeyTitle', { + ? i18n.translate('xpack.searchIndices.shared.codeView.apiKeyTitle', { defaultMessage: 'Copy your API key', }) - : i18n.translate('xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyTitle', { + : i18n.translate('xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyTitle', { defaultMessage: 'Create an API key', }); const description = apiKey - ? i18n.translate('xpack.searchIndices.startPage.codeView.apiKeyDescription', { + ? i18n.translate('xpack.searchIndices.shared.codeView.apiKeyDescription', { defaultMessage: 'Make sure you keep it somewhere safe. You won’t be able to retrieve it later.', }) - : i18n.translate('xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyDescription', { + : i18n.translate('xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyDescription', { defaultMessage: 'Create an API key to connect to Elasticsearch.', }); diff --git a/x-pack/plugins/search_indices/public/components/shared/breadcrumbs.ts b/x-pack/plugins/search_indices/public/components/shared/breadcrumbs.ts new file mode 100644 index 000000000000..2805100d6cab --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/shared/breadcrumbs.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChromeBreadcrumb } from '@kbn/core-chrome-browser'; +import { i18n } from '@kbn/i18n'; + +export const IndexManagementBreadcrumbs: ChromeBreadcrumb[] = [ + { + text: i18n.translate('xpack.searchIndices.breadcrumbs.indexManagement.label', { + defaultMessage: 'Index Management', + }), + href: '/app/management/data/index_management', + }, + { + text: i18n.translate('xpack.searchIndices.breadcrumbs.indexManagement.indices.label', { + defaultMessage: 'Indices', + }), + href: '/app/management/data/index_management/indices', + }, +]; diff --git a/x-pack/plugins/search_indices/public/components/start/create_index_code.tsx b/x-pack/plugins/search_indices/public/components/shared/create_index_code_view.tsx similarity index 67% rename from x-pack/plugins/search_indices/public/components/start/create_index_code.tsx rename to x-pack/plugins/search_indices/public/components/shared/create_index_code_view.tsx index fadfe1c7dcb9..14e9162fb970 100644 --- a/x-pack/plugins/search_indices/public/components/start/create_index_code.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/create_index_code_view.tsx @@ -4,61 +4,55 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { TryInConsoleButton } from '@kbn/try-in-console'; import { useSearchApiKey } from '@kbn/search-api-keys-components'; -import { AnalyticsEvents } from '../../analytics/constants'; import { Languages, AvailableLanguages, LanguageOptions } from '../../code_examples'; import { useUsageTracker } from '../../hooks/use_usage_tracker'; import { useKibana } from '../../hooks/use_kibana'; import { useElasticsearchUrl } from '../../hooks/use_elasticsearch_url'; -import { CodeSample } from '../shared/code_sample'; -import { LanguageSelector } from '../shared/language_selector'; - -import { CreateIndexFormState } from './types'; -import { useStartPageCodingExamples } from './hooks/use_coding_examples'; import { APIKeyCallout } from './api_key_callout'; +import { CodeSample } from './code_sample'; +import { useCreateIndexCodingExamples } from './hooks/use_create_index_coding_examples'; +import { LanguageSelector } from './language_selector'; export interface CreateIndexCodeViewProps { - createIndexForm: CreateIndexFormState; + selectedLanguage: AvailableLanguages; + indexName: string; changeCodingLanguage: (language: AvailableLanguages) => void; canCreateApiKey?: boolean; + analyticsEvents: { + runInConsole: string; + installCommands: string; + createIndex: string; + }; } export const CreateIndexCodeView = ({ - createIndexForm, - changeCodingLanguage, + analyticsEvents, canCreateApiKey, + changeCodingLanguage, + indexName, + selectedLanguage, }: CreateIndexCodeViewProps) => { const { application, share, console: consolePlugin } = useKibana().services; const usageTracker = useUsageTracker(); - const selectedCodeExamples = useStartPageCodingExamples(); + const selectedCodeExamples = useCreateIndexCodingExamples(); - const { codingLanguage: selectedLanguage } = createIndexForm; - const onSelectLanguage = useCallback( - (value: AvailableLanguages) => { - changeCodingLanguage(value); - usageTracker.count([ - AnalyticsEvents.startCreateIndexLanguageSelect, - `${AnalyticsEvents.startCreateIndexLanguageSelect}_${value}`, - ]); - }, - [usageTracker, changeCodingLanguage] - ); const elasticsearchUrl = useElasticsearchUrl(); const { apiKey, apiKeyIsVisible } = useSearchApiKey(); const codeParams = useMemo(() => { return { - indexName: createIndexForm.indexName || undefined, + indexName: indexName || undefined, elasticsearchURL: elasticsearchUrl, apiKey: apiKeyIsVisible && apiKey ? apiKey : undefined, }; - }, [createIndexForm.indexName, elasticsearchUrl, apiKeyIsVisible, apiKey]); + }, [indexName, elasticsearchUrl, apiKeyIsVisible, apiKey]); const selectedCodeExample = useMemo(() => { return selectedCodeExamples[selectedLanguage]; }, [selectedLanguage, selectedCodeExamples]); @@ -75,7 +69,7 @@ export const CreateIndexCodeView = ({ @@ -87,8 +81,8 @@ export const CreateIndexCodeView = ({ telemetryId={`${selectedLanguage}_create_index`} onClick={() => { usageTracker.click([ - AnalyticsEvents.startCreateIndexRunInConsole, - `${AnalyticsEvents.startCreateIndexRunInConsole}_${selectedLanguage}`, + analyticsEvents.runInConsole, + `${analyticsEvents.runInConsole}_${selectedLanguage}`, ]); }} /> @@ -102,8 +96,8 @@ export const CreateIndexCodeView = ({ code={selectedCodeExample.installCommand} onCodeCopyClick={() => { usageTracker.click([ - AnalyticsEvents.startCreateIndexCodeCopyInstall, - `${AnalyticsEvents.startCreateIndexCodeCopyInstall}_${selectedLanguage}`, + analyticsEvents.installCommands, + `${analyticsEvents.installCommands}_${selectedLanguage}`, ]); }} /> @@ -116,9 +110,9 @@ export const CreateIndexCodeView = ({ code={selectedCodeExample.createIndex(codeParams)} onCodeCopyClick={() => { usageTracker.click([ - AnalyticsEvents.startCreateIndexCodeCopy, - `${AnalyticsEvents.startCreateIndexCodeCopy}_${selectedLanguage}`, - `${AnalyticsEvents.startCreateIndexCodeCopy}_${selectedLanguage}_${selectedCodeExamples.exampleType}`, + analyticsEvents.createIndex, + `${analyticsEvents.createIndex}_${selectedLanguage}`, + `${analyticsEvents.createIndex}_${selectedLanguage}_${selectedCodeExamples.exampleType}`, ]); }} /> diff --git a/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx b/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx new file mode 100644 index 000000000000..ba2f83cb273d --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + EuiButton, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiHorizontalRule, + EuiIcon, + EuiLink, + EuiPanel, + EuiSpacer, + EuiText, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { UserStartPrivilegesResponse } from '../../../common'; + +export interface CreateIndexFormProps { + indexName: string; + indexNameHasError: boolean; + isLoading: boolean; + onCreateIndex: (e: React.FormEvent) => void; + onFileUpload: () => void; + onIndexNameChange: (e: React.ChangeEvent) => void; + showAPIKeyCreateLabel: boolean; + userPrivileges?: UserStartPrivilegesResponse; +} + +export const CreateIndexForm = ({ + indexName, + indexNameHasError, + isLoading, + onCreateIndex, + onFileUpload, + onIndexNameChange, + showAPIKeyCreateLabel, + userPrivileges, +}: CreateIndexFormProps) => { + return ( + <> + + + + + + + + + {i18n.translate('xpack.searchIndices.shared.createIndex.permissionTooltip', { + defaultMessage: 'You do not have permission to create an index.', + })} +

+ ) : undefined + } + > + + {i18n.translate('xpack.searchIndices.shared.createIndex.action.text', { + defaultMessage: 'Create my index', + })} + +
+
+ + {showAPIKeyCreateLabel && ( + + + +

+ {i18n.translate( + 'xpack.searchIndices.shared.createIndex.apiKeyCreation.description', + { + defaultMessage: "We'll create an API key for this index", + } + )} +

+
+
+ )} +
+
+
+ + + + + + + + +

+ + {i18n.translate('xpack.searchIndices.shared.createIndex.fileUpload.link', { + defaultMessage: 'Upload a file', + })} + + ), + }} + /> +

+
+
+
+
+ + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/shared/create_index_panel.tsx b/x-pack/plugins/search_indices/public/components/shared/create_index_panel.tsx new file mode 100644 index 000000000000..8c353ebab6bd --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/shared/create_index_panel.tsx @@ -0,0 +1,271 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { + EuiButtonEmpty, + EuiButtonGroup, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSpacer, + EuiText, + EuiTextAlign, + EuiTitle, + useEuiTheme, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { docLinks } from '../../../common/doc_links'; +import { useKibana } from '../../hooks/use_kibana'; +import { CreateIndexViewMode } from '../../types'; + +const MAX_WIDTH = '650px'; + +export interface CreateIndexPanelProps { + children: React.ReactNode | React.ReactNode[]; + createIndexView: CreateIndexViewMode; + onChangeView: (id: string) => void; + onClose: () => void; + showCallouts?: boolean; + showSkip?: boolean; + title?: React.ReactNode; +} + +export const CreateIndexPanel = ({ + children, + createIndexView, + onChangeView, + onClose, + showCallouts, + showSkip, + title, +}: CreateIndexPanelProps) => { + const { cloud, http } = useKibana().services; + const { euiTheme } = useEuiTheme(); + + const o11yTrialLink = useMemo(() => { + if (cloud && cloud.isServerlessEnabled) { + const baseUrl = cloud?.projectsUrl ?? 'https://cloud.elastic.co/projects/'; + return `${baseUrl}create/observability/start`; + } + return http.basePath.prepend('/app/observability/onboarding'); + }, [cloud, http]); + + return ( + <> + + + + + + + + + + + +

+ {i18n.translate('xpack.searchIndices.shared.createIndex.pageTitle', { + defaultMessage: 'Elasticsearch', + })} +

+
+
+
+ + +

+ {i18n.translate('xpack.searchIndices.shared.createIndex.pageDescription', { + defaultMessage: 'Get started with Elasticsearch', + })} +

+
+
+ + + + + + +

+ {title ?? + i18n.translate('xpack.searchIndices.shared.createIndex.defaultTitle', { + defaultMessage: 'Create an index', + })} +

+
+
+ + + +
+ +

+ {i18n.translate('xpack.searchIndices.shared.createIndex.description', { + defaultMessage: + 'An index stores your data and defines the schema, or field mappings, for your searches', + })} +

+
+ {children} +
+
+ {showCallouts && ( + <> + + + + +
+ {i18n.translate( + 'xpack.searchIndices.shared.createIndex.observabilityCallout.title', + { + defaultMessage: 'Looking to store your logs or metrics data?', + } + )} +
+
+
+ + + + + {i18n.translate( + 'xpack.searchIndices.shared.createIndex.observabilityCallout.logs.button', + { + defaultMessage: 'Collect and analyze logs', + } + )} + + + + {i18n.translate( + 'xpack.searchIndices.shared.createIndex.observabilityCallout.logs.subTitle', + { + defaultMessage: 'Explore Logstash and Beats', + } + )} + + + + + or + + + + {i18n.translate( + 'xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.button', + { + defaultMessage: 'Start an Observability trial', + } + )} + + + + {i18n.translate( + 'xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.subTitle', + { + defaultMessage: 'Powerful performance monitoring', + } + )} + + + + +
+ + )} +
+ {showSkip === true && ( + <> + + + + {i18n.translate('xpack.searchIndices.shared.createIndex.skipLabel', { + defaultMessage: 'Skip', + })} + + + + )} + + ); +}; diff --git a/x-pack/plugins/search_indices/public/components/start/hooks/use_create_index.tsx b/x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index.tsx similarity index 94% rename from x-pack/plugins/search_indices/public/components/start/hooks/use_create_index.tsx rename to x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index.tsx index 8daafec5573c..537aa3cc4b98 100644 --- a/x-pack/plugins/search_indices/public/components/start/hooks/use_create_index.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index.tsx @@ -11,7 +11,7 @@ import { useCreateIndex as useCreateIndexApi } from '../../../hooks/api/use_crea import { useKibana } from '../../../hooks/use_kibana'; -import { navigateToIndexDetails } from './utils'; +import { navigateToIndexDetails } from '../../utils'; export const useCreateIndex = () => { const { application, http } = useKibana().services; diff --git a/x-pack/plugins/search_indices/public/components/start/hooks/use_coding_examples.tsx b/x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index_coding_examples.tsx similarity index 87% rename from x-pack/plugins/search_indices/public/components/start/hooks/use_coding_examples.tsx rename to x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index_coding_examples.tsx index 1a351d10943f..fb1cb6a7eab5 100644 --- a/x-pack/plugins/search_indices/public/components/start/hooks/use_coding_examples.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/hooks/use_create_index_coding_examples.tsx @@ -8,7 +8,7 @@ import { CreateIndexCodeExamples } from '../../../types'; import { DenseVectorSeverlessCodeExamples } from '../../../code_examples/create_index'; -export const useStartPageCodingExamples = (): CreateIndexCodeExamples => { +export const useCreateIndexCodingExamples = (): CreateIndexCodeExamples => { // TODO: in the future this will be dynamic based on the onboarding token // or project sub-type return DenseVectorSeverlessCodeExamples; diff --git a/x-pack/plugins/search_indices/public/components/start/status_error.tsx b/x-pack/plugins/search_indices/public/components/shared/load_indices_status_error.tsx similarity index 79% rename from x-pack/plugins/search_indices/public/components/start/status_error.tsx rename to x-pack/plugins/search_indices/public/components/shared/load_indices_status_error.tsx index 7e41e37d5cd9..58e1867cc577 100644 --- a/x-pack/plugins/search_indices/public/components/start/status_error.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/load_indices_status_error.tsx @@ -15,14 +15,14 @@ export interface StartPageErrorProps { error: unknown; } -export const StartPageError = ({ error }: StartPageErrorProps) => { +export const LoadIndicesStatusError = ({ error }: StartPageErrorProps) => { return ( - {i18n.translate('xpack.searchIndices.startPage.statusFetchError.title', { + {i18n.translate('xpack.searchIndices.shared.statusFetchError.title', { defaultMessage: 'Error loading indices', })} @@ -31,7 +31,7 @@ export const StartPageError = ({ error }: StartPageErrorProps) => { {getErrorMessage( error, - i18n.translate('xpack.searchIndices.startPage.statusFetchError.unknownError', { + i18n.translate('xpack.searchIndices.shared.statusFetchError.unknownError', { defaultMessage: 'Unknown error fetching indices.', }) )} diff --git a/x-pack/plugins/search_indices/public/components/start/create_index.tsx b/x-pack/plugins/search_indices/public/components/start/create_index.tsx index 788bd1e36f2e..e7ca978fb8ea 100644 --- a/x-pack/plugins/search_indices/public/components/start/create_index.tsx +++ b/x-pack/plugins/search_indices/public/components/start/create_index.tsx @@ -6,52 +6,29 @@ */ import React, { useCallback, useState } from 'react'; -import { - EuiButton, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiForm, - EuiFormRow, - EuiHorizontalRule, - EuiIcon, - EuiLink, - EuiPanel, - EuiSpacer, - EuiText, - EuiToolTip, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import type { UserStartPrivilegesResponse } from '../../../common'; import { AnalyticsEvents } from '../../analytics/constants'; import { useUsageTracker } from '../../hooks/use_usage_tracker'; +import { CreateIndexFormState } from '../../types'; import { isValidIndexName } from '../../utils/indices'; -import { useCreateIndex } from './hooks/use_create_index'; +import { useCreateIndex } from '../shared/hooks/use_create_index'; +import { CreateIndexForm } from '../shared/create_index_form'; -import { CreateIndexFormState } from './types'; import { useKibana } from '../../hooks/use_kibana'; -const CREATE_INDEX_CONTENT = i18n.translate( - 'xpack.searchIndices.startPage.createIndex.action.text', - { - defaultMessage: 'Create my index', - } -); - -export interface CreateIndexFormProps { +export interface CreateIndexUIViewProps { formState: CreateIndexFormState; setFormState: React.Dispatch>; userPrivileges?: UserStartPrivilegesResponse; } -export const CreateIndexForm = ({ +export const CreateIndexUIView = ({ userPrivileges, formState, setFormState, -}: CreateIndexFormProps) => { +}: CreateIndexUIViewProps) => { const { application } = useKibana().services; const [indexNameHasError, setIndexNameHasError] = useState(false); const usageTracker = useUsageTracker(); @@ -86,129 +63,15 @@ export const CreateIndexForm = ({ }, [usageTracker, application]); return ( - <> - - - - - - - - {userPrivileges?.privileges?.canCreateIndex === false ? ( - - {i18n.translate('xpack.searchIndices.startPage.createIndex.permissionTooltip', { - defaultMessage: 'You do not have permission to create an index.', - })} -

- } - > - - {CREATE_INDEX_CONTENT} - -
- ) : ( - - {CREATE_INDEX_CONTENT} - - )} -
- - {userPrivileges?.privileges?.canCreateApiKeys && ( - - - -

- {i18n.translate( - 'xpack.searchIndices.startPage.createIndex.apiKeyCreation.description', - { - defaultMessage: "We'll create an API key for this index", - } - )} -

-
-
- )} -
-
-
- - - - - - - - -

- - {i18n.translate( - 'xpack.searchIndices.startPage.createIndex.fileUpload.link', - { - defaultMessage: 'Upload a file', - } - )} - - ), - }} - /> -

-
-
-
-
- + ); }; diff --git a/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx b/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx index 42b021043cb3..3f3063ddb150 100644 --- a/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx +++ b/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx @@ -5,23 +5,10 @@ * 2.0. */ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { - EuiButtonEmpty, - EuiButtonGroup, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiSpacer, - EuiText, - EuiTextAlign, - EuiTitle, -} from '@elastic/eui'; +import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import type { IndicesStatusResponse, UserStartPrivilegesResponse } from '../../../common'; -import { docLinks } from '../../../common/doc_links'; import { AnalyticsEvents } from '../../analytics/constants'; import { AvailableLanguages } from '../../code_examples'; @@ -29,9 +16,11 @@ import { useUsageTracker } from '../../hooks/use_usage_tracker'; import { generateRandomIndexName } from '../../utils/indices'; import { getDefaultCodingLanguage } from '../../utils/language'; -import { CreateIndexForm } from './create_index'; -import { CreateIndexCodeView } from './create_index_code'; -import { CreateIndexFormState } from './types'; +import { CreateIndexUIView } from './create_index'; +import { CreateIndexCodeView } from '../shared/create_index_code_view'; +import { CreateIndexFormState, CreateIndexViewMode } from '../../types'; + +import { CreateIndexPanel } from '../shared/create_index_panel'; import { useKibana } from '../../hooks/use_kibana'; function initCreateIndexState(): CreateIndexFormState { @@ -43,21 +32,17 @@ function initCreateIndexState(): CreateIndexFormState { }; } -const MAX_WIDTH = '650px'; - -enum CreateIndexView { - UI = 'ui', - Code = 'code', -} export interface ElasticsearchStartProps { indicesData?: IndicesStatusResponse; userPrivileges?: UserStartPrivilegesResponse; } export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps) => { - const { cloud, http } = useKibana().services; - const [createIndexView, setCreateIndexView] = useState( - userPrivileges?.privileges.canCreateIndex === false ? CreateIndexView.Code : CreateIndexView.UI + const { application } = useKibana().services; + const [createIndexView, setCreateIndexViewMode] = useState( + userPrivileges?.privileges.canCreateIndex === false + ? CreateIndexViewMode.Code + : CreateIndexViewMode.UI ); const [formState, setFormState] = useState(initCreateIndexState); const usageTracker = useUsageTracker(); @@ -68,28 +53,20 @@ export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps) useEffect(() => { if (userPrivileges === undefined) return; if (userPrivileges.privileges.canCreateIndex === false) { - setCreateIndexView(CreateIndexView.Code); + setCreateIndexViewMode(CreateIndexViewMode.Code); } }, [userPrivileges]); - const o11yTrialLink = useMemo(() => { - if (cloud && cloud.isServerlessEnabled) { - const baseUrl = cloud?.projectsUrl ?? 'https://cloud.elastic.co/projects/'; - return `${baseUrl}create/observability/start`; - } - return http.basePath.prepend('/app/observability/onboarding'); - }, [cloud, http]); - const onChangeView = useCallback( (id: string) => { switch (id) { - case CreateIndexView.UI: + case CreateIndexViewMode.UI: usageTracker.click(AnalyticsEvents.startPageShowCreateIndexUIClick); - setCreateIndexView(CreateIndexView.UI); + setCreateIndexViewMode(CreateIndexViewMode.UI); return; - case CreateIndexView.Code: + case CreateIndexViewMode.Code: usageTracker.click(AnalyticsEvents.startPageShowCodeClick); - setCreateIndexView(CreateIndexView.Code); + setCreateIndexViewMode(CreateIndexViewMode.Code); return; } }, @@ -101,178 +78,48 @@ export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps) ...formState, codingLanguage: language, }); + usageTracker.count([ + AnalyticsEvents.startCreateIndexLanguageSelect, + `${AnalyticsEvents.startCreateIndexLanguageSelect}_${language}`, + ]); }, - [formState, setFormState] + [usageTracker, formState, setFormState] ); + const onClose = useCallback(() => { + application.navigateToApp('management', { deepLinkId: 'index_management' }); + }, [application]); return ( - - - - - - - - -

- {i18n.translate('xpack.searchIndices.startPage.pageTitle', { - defaultMessage: 'Elasticsearch', - })} -

-
-
-
- - -

- {i18n.translate('xpack.searchIndices.startPage.pageDescription', { - defaultMessage: 'Vectorize, search, and visualize your data', - })} -

-
-
- - - - - - -

- {i18n.translate('xpack.searchIndices.startPage.createIndex.title', { - defaultMessage: 'Create your first index', - })} -

-
-
- - - -
- -

- {i18n.translate('xpack.searchIndices.startPage.createIndex.description', { - defaultMessage: - 'An index stores your data and defines the schema, or field mappings, for your searches', - })} -

-
- {createIndexView === CreateIndexView.UI && ( - - )} - {createIndexView === CreateIndexView.Code && ( - - )} -
-
- - - - -
- {i18n.translate('xpack.searchIndices.startPage.observabilityCallout.title', { - defaultMessage: 'Looking to store your logs or metrics data?', - })} -
-
-
- - - - - {i18n.translate('xpack.searchIndices.startPage.observabilityCallout.logs.button', { - defaultMessage: 'Collect and analyze logs', - })} - - - - {i18n.translate( - 'xpack.searchIndices.startPage.observabilityCallout.logs.subTitle', - { - defaultMessage: 'Explore Logstash and Beats', - } - )} - - - - - or - - - - {i18n.translate( - 'xpack.searchIndices.startPage.observabilityCallout.o11yTrial.button', - { - defaultMessage: 'Start an Observability trial', - } - )} - - - - {i18n.translate( - 'xpack.searchIndices.startPage.observabilityCallout.o11yTrial.subTitle', - { - defaultMessage: 'Powerful performance monitoring', - } - )} - - - - -
-
+ {createIndexView === CreateIndexViewMode.UI && ( + + )} + {createIndexView === CreateIndexViewMode.Code && ( + + )} + ); }; diff --git a/x-pack/plugins/search_indices/public/components/start/hooks/use_indices_redirect.tsx b/x-pack/plugins/search_indices/public/components/start/hooks/use_indices_redirect.tsx index 899d44da2afa..6909b1117e32 100644 --- a/x-pack/plugins/search_indices/public/components/start/hooks/use_indices_redirect.tsx +++ b/x-pack/plugins/search_indices/public/components/start/hooks/use_indices_redirect.tsx @@ -11,7 +11,7 @@ import type { IndicesStatusResponse } from '../../../../common'; import { useKibana } from '../../../hooks/use_kibana'; -import { navigateToIndexDetails } from './utils'; +import { navigateToIndexDetails } from '../../utils'; import { useUsageTracker } from '../../../contexts/usage_tracker_context'; import { AnalyticsEvents } from '../../../analytics/constants'; diff --git a/x-pack/plugins/search_indices/public/components/start/start_page.tsx b/x-pack/plugins/search_indices/public/components/start/start_page.tsx index 4a848f580d22..4dabec2e5fa9 100644 --- a/x-pack/plugins/search_indices/public/components/start/start_page.tsx +++ b/x-pack/plugins/search_indices/public/components/start/start_page.tsx @@ -6,6 +6,7 @@ */ import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiLoadingLogo, EuiPageTemplate } from '@elastic/eui'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; @@ -16,7 +17,13 @@ import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; import { useIndicesRedirect } from './hooks/use_indices_redirect'; import { ElasticsearchStart } from './elasticsearch_start'; -import { StartPageError } from './status_error'; +import { LoadIndicesStatusError } from '../shared/load_indices_status_error'; +import { IndexManagementBreadcrumbs } from '../shared/breadcrumbs'; +import { usePageChrome } from '../../hooks/use_page_chrome'; + +const PageTitle = i18n.translate('xpack.searchIndices.startPage.docTitle', { + defaultMessage: 'Create your first index', +}); export const ElasticsearchStartPage = () => { const { console: consolePlugin } = useKibana().services; @@ -27,6 +34,7 @@ export const ElasticsearchStartPage = () => { error: indicesFetchError, } = useIndicesStatusQuery(); const { data: userPrivileges } = useUserPrivilegesQuery(); + usePageChrome(PageTitle, [...IndexManagementBreadcrumbs, { text: PageTitle }]); const embeddableConsole = useMemo( () => (consolePlugin?.EmbeddableConsole ? : null), @@ -43,7 +51,7 @@ export const ElasticsearchStartPage = () => { > {isInitialLoading && } - {hasIndicesStatusFetchError && } + {hasIndicesStatusFetchError && } {!isInitialLoading && !hasIndicesStatusFetchError && ( )} diff --git a/x-pack/plugins/search_indices/public/components/start/types.ts b/x-pack/plugins/search_indices/public/components/start/types.ts deleted file mode 100644 index 4c0235ec515f..000000000000 --- a/x-pack/plugins/search_indices/public/components/start/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AvailableLanguages } from '../../code_examples'; - -export interface CreateIndexFormState { - indexName: string; - defaultIndexName: string; - codingLanguage: AvailableLanguages; -} diff --git a/x-pack/plugins/search_indices/public/components/start/hooks/utils.ts b/x-pack/plugins/search_indices/public/components/utils.ts similarity index 66% rename from x-pack/plugins/search_indices/public/components/start/hooks/utils.ts rename to x-pack/plugins/search_indices/public/components/utils.ts index ed8b6f40e51f..235c03b9faab 100644 --- a/x-pack/plugins/search_indices/public/components/start/hooks/utils.ts +++ b/x-pack/plugins/search_indices/public/components/utils.ts @@ -5,12 +5,15 @@ * 2.0. */ +import { generatePath } from 'react-router-dom'; + import type { ApplicationStart, HttpSetup } from '@kbn/core/public'; +import { INDICES_APP_BASE, SEARCH_INDICES_DETAILS_PATH } from '../routes'; -const INDEX_DETAILS_PATH = '/app/elasticsearch/indices/index_details'; +const INDEX_DETAILS_FULL_PATH = `${INDICES_APP_BASE}${SEARCH_INDICES_DETAILS_PATH}`; function getIndexDetailsPath(http: HttpSetup, indexName: string) { - return http.basePath.prepend(`${INDEX_DETAILS_PATH}/${encodeURIComponent(indexName)}`); + return http.basePath.prepend(generatePath(INDEX_DETAILS_FULL_PATH, { indexName })); } export const navigateToIndexDetails = ( diff --git a/x-pack/plugins/search_indices/public/hooks/use_page_chrome.ts b/x-pack/plugins/search_indices/public/hooks/use_page_chrome.ts new file mode 100644 index 000000000000..fae438b502a0 --- /dev/null +++ b/x-pack/plugins/search_indices/public/hooks/use_page_chrome.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import type { ChromeBreadcrumb } from '@kbn/core-chrome-browser'; +import { useKibana } from './use_kibana'; + +export const usePageChrome = (docTitle: string, breadcrumbs: ChromeBreadcrumb[]) => { + const { chrome, http, serverless } = useKibana().services; + + useEffect(() => { + chrome.docTitle.change(docTitle); + const newBreadcrumbs = breadcrumbs.map((breadcrumb) => { + if (breadcrumb.href && http.basePath.get().length > 0) { + breadcrumb.href = http.basePath.prepend(breadcrumb.href); + } + return breadcrumb; + }); + if (serverless) { + serverless.setBreadcrumbs(newBreadcrumbs); + } else { + chrome.setBreadcrumbs(newBreadcrumbs); + } + return () => { + // clear manually set breadcrumbs + if (serverless) { + serverless.setBreadcrumbs([]); + } else { + chrome.setBreadcrumbs([]); + } + }; + }, [breadcrumbs, chrome, docTitle, http.basePath, serverless]); +}; diff --git a/x-pack/plugins/search_indices/public/locators.ts b/x-pack/plugins/search_indices/public/locators.ts new file mode 100644 index 000000000000..587ce51f2c82 --- /dev/null +++ b/x-pack/plugins/search_indices/public/locators.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LocatorDefinition } from '@kbn/share-plugin/common'; +import type { SharePluginSetup } from '@kbn/share-plugin/public'; +import type { SerializableRecord } from '@kbn/utility-types'; + +import { INDICES_APP_ID } from '../common'; +import { CREATE_INDEX_PATH } from './routes'; + +export function registerLocators(share: SharePluginSetup) { + share.url.locators.create(new CreateIndexLocatorDefinition()); +} + +class CreateIndexLocatorDefinition implements LocatorDefinition { + public readonly getLocation = async () => { + return { + app: INDICES_APP_ID, + path: CREATE_INDEX_PATH, + state: {}, + }; + }; + + public readonly id = 'SEARCH_CREATE_INDEX'; +} diff --git a/x-pack/plugins/search_indices/public/plugin.ts b/x-pack/plugins/search_indices/public/plugin.ts index c9b5c8f4c765..b92fbaa5e7f4 100644 --- a/x-pack/plugins/search_indices/public/plugin.ts +++ b/x-pack/plugins/search_indices/public/plugin.ts @@ -6,10 +6,12 @@ */ import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { SEARCH_INDICES_CREATE_INDEX } from '@kbn/deeplinks-search/constants'; import { i18n } from '@kbn/i18n'; import { docLinks } from '../common/doc_links'; import type { + AppPluginSetupDependencies, SearchIndicesAppPluginStartDependencies, SearchIndicesPluginSetup, SearchIndicesPluginStart, @@ -17,7 +19,13 @@ import type { } from './types'; import { initQueryClient } from './services/query_client'; import { INDICES_APP_ID, START_APP_ID } from '../common'; -import { INDICES_APP_BASE, START_APP_BASE, SearchIndexDetailsTabValues } from './routes'; +import { + CREATE_INDEX_PATH, + INDICES_APP_BASE, + START_APP_BASE, + SearchIndexDetailsTabValues, +} from './routes'; +import { registerLocators } from './locators'; export class SearchIndicesPlugin implements Plugin @@ -25,7 +33,8 @@ export class SearchIndicesPlugin private pluginEnabled: boolean = false; public setup( - core: CoreSetup + core: CoreSetup, + plugins: AppPluginSetupDependencies ): SearchIndicesPluginSetup { this.pluginEnabled = true; @@ -51,12 +60,21 @@ export class SearchIndicesPlugin core.application.register({ id: INDICES_APP_ID, appRoute: INDICES_APP_BASE, + deepLinks: [ + { + id: SEARCH_INDICES_CREATE_INDEX, + path: CREATE_INDEX_PATH, + title: i18n.translate('xpack.searchIndices.elasticsearchIndices.createIndexTitle', { + defaultMessage: 'Create index', + }), + }, + ], title: i18n.translate('xpack.searchIndices.elasticsearchIndices.startAppTitle', { defaultMessage: 'Elasticsearch Indices', }), async mount({ element, history }) { const { renderApp } = await import('./application'); - const { SearchIndicesRouter } = await import('./components/indices/indices_router'); + const { SearchIndicesRouter } = await import('./components/indices_router'); const [coreStart, depsStart] = await core.getStartServices(); const startDeps: SearchIndicesServicesContextDeps = { ...depsStart, @@ -66,6 +84,8 @@ export class SearchIndicesPlugin }, }); + registerLocators(plugins.share); + return { enabled: true, startAppId: START_APP_ID, diff --git a/x-pack/plugins/search_indices/public/routes.ts b/x-pack/plugins/search_indices/public/routes.ts index 057891d63226..86d05fb73032 100644 --- a/x-pack/plugins/search_indices/public/routes.ts +++ b/x-pack/plugins/search_indices/public/routes.ts @@ -13,6 +13,7 @@ export enum SearchIndexDetailsTabs { MAPPINGS = 'mappings', SETTINGS = 'settings', } +export const CREATE_INDEX_PATH = `${ROOT_PATH}create`; export const SearchIndexDetailsTabValues: string[] = Object.values(SearchIndexDetailsTabs); export const START_APP_BASE = '/app/elasticsearch/start'; diff --git a/x-pack/plugins/search_indices/public/types.ts b/x-pack/plugins/search_indices/public/types.ts index cfc732adff45..ccf4d56e13a6 100644 --- a/x-pack/plugins/search_indices/public/types.ts +++ b/x-pack/plugins/search_indices/public/types.ts @@ -5,19 +5,25 @@ * 2.0. */ -import type { CloudStart } from '@kbn/cloud-plugin/public'; -import type { ConsolePluginStart } from '@kbn/console-plugin/public'; +import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; +import type { ConsolePluginSetup, ConsolePluginStart } from '@kbn/console-plugin/public'; import type { AppMountParameters, CoreStart } from '@kbn/core/public'; -import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; -import type { SharePluginStart } from '@kbn/share-plugin/public'; -import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import type { + UsageCollectionSetup, + UsageCollectionStart, +} from '@kbn/usage-collection-plugin/public'; import type { MappingProperty, MappingPropertyBase, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { IndexManagementPluginStart } from '@kbn/index-management-shared-types'; +import type { + IndexManagementPluginSetup, + IndexManagementPluginStart, +} from '@kbn/index-management-shared-types'; import type { AppDeepLinkId } from '@kbn/core-chrome-browser'; -import { ServerlessPluginStart } from '@kbn/serverless/public'; +import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import type { AvailableLanguages } from './code_examples'; export interface SearchIndicesPluginSetup { enabled: boolean; @@ -31,14 +37,20 @@ export interface SearchIndicesPluginStart { startRoute: string; } -export interface AppPluginStartDependencies { - navigation: NavigationPublicPluginStart; +export interface AppPluginSetupDependencies { + console?: ConsolePluginSetup; + cloud?: CloudSetup; + indexManagement: IndexManagementPluginSetup; + share: SharePluginSetup; + serverless?: ServerlessPluginSetup; + usageCollection?: UsageCollectionSetup; } export interface SearchIndicesAppPluginStartDependencies { console?: ConsolePluginStart; cloud?: CloudStart; share: SharePluginStart; + serverless?: ServerlessPluginStart; usageCollection?: UsageCollectionStart; indexManagement: IndexManagementPluginStart; } @@ -50,8 +62,6 @@ export interface SearchIndicesServicesContextDeps { export type SearchIndicesServicesContext = CoreStart & SearchIndicesAppPluginStartDependencies & { history: AppMountParameters['history']; - indexManagement: IndexManagementPluginStart; - serverless: ServerlessPluginStart; }; export interface AppUsageTracker { @@ -123,3 +133,14 @@ export interface IngestDataCodeExamples { python: IngestDataCodeDefinition; javascript: IngestDataCodeDefinition; } + +export interface CreateIndexFormState { + indexName: string; + defaultIndexName: string; + codingLanguage: AvailableLanguages; +} + +export enum CreateIndexViewMode { + UI = 'ui', + Code = 'code', +} diff --git a/x-pack/plugins/search_indices/public/utils/indices.test.ts b/x-pack/plugins/search_indices/public/utils/indices.test.ts index 8ddd7cbb56fc..a3b6654a1209 100644 --- a/x-pack/plugins/search_indices/public/utils/indices.test.ts +++ b/x-pack/plugins/search_indices/public/utils/indices.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { generateRandomIndexName, isValidIndexName } from './indices'; +import { generateRandomIndexName, isValidIndexName, getFirstNewIndexName } from './indices'; describe('indices utils', function () { describe('generateRandomIndexName', function () { @@ -46,4 +46,31 @@ describe('indices utils', function () { expect(isValidIndexName(indexName)).toBe(true); }); }); + + describe('getFirstNewIndexName', function () { + it('returns undefined when lists are the same', () => { + expect(getFirstNewIndexName([], [])).toEqual(undefined); + expect(getFirstNewIndexName(['index'], ['index'])).toEqual(undefined); + expect(getFirstNewIndexName(['index', 'test'], ['index', 'test'])).toEqual(undefined); + }); + + it('returns new item when it exists', () => { + expect(getFirstNewIndexName([], ['index'])).toEqual('index'); + expect(getFirstNewIndexName(['index'], ['index', 'test'])).toEqual('test'); + expect(getFirstNewIndexName(['index', 'test'], ['index', 'test', 'unit-test'])).toEqual( + 'unit-test' + ); + expect(getFirstNewIndexName(['index', 'test'], ['unit-test', 'index', 'test'])).toEqual( + 'unit-test' + ); + }); + it('returns first new item when it multiple new indices exists', () => { + expect(getFirstNewIndexName([], ['index', 'test'])).toEqual('index'); + expect(getFirstNewIndexName(['index'], ['test', 'index', 'unit-test'])).toEqual('test'); + }); + it('can handle old indices being removed', () => { + expect(getFirstNewIndexName(['index'], ['test'])).toEqual('test'); + expect(getFirstNewIndexName(['test', 'index', 'unit-test'], ['index', 'new'])).toEqual('new'); + }); + }); }); diff --git a/x-pack/plugins/search_indices/public/utils/indices.ts b/x-pack/plugins/search_indices/public/utils/indices.ts index 21c6e672af08..3812eea8757b 100644 --- a/x-pack/plugins/search_indices/public/utils/indices.ts +++ b/x-pack/plugins/search_indices/public/utils/indices.ts @@ -35,3 +35,12 @@ export function generateRandomIndexName( return result; } + +export function getFirstNewIndexName(startingIndexNames: string[], currentIndexNames: string[]) { + for (const index of currentIndexNames) { + if (startingIndexNames.indexOf(index) === -1) { + return index; + } + } + return undefined; +} diff --git a/x-pack/plugins/search_indices/tsconfig.json b/x-pack/plugins/search_indices/tsconfig.json index 61b82f448549..341dd230cee5 100644 --- a/x-pack/plugins/search_indices/tsconfig.json +++ b/x-pack/plugins/search_indices/tsconfig.json @@ -12,7 +12,6 @@ ], "kbn_references": [ "@kbn/core", - "@kbn/navigation-plugin", "@kbn/config-schema", "@kbn/core-elasticsearch-server", "@kbn/logging", @@ -39,7 +38,8 @@ "@kbn/search-shared-ui", "@kbn/deeplinks-search", "@kbn/core-chrome-browser", - "@kbn/serverless" + "@kbn/serverless", + "@kbn/utility-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx index d4a165f56266..5bc9f84c833f 100644 --- a/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx +++ b/x-pack/plugins/search_playground/public/components/setup_page/create_index_button.tsx @@ -16,7 +16,9 @@ export const CreateIndexButton: React.FC = () => { services: { application, share }, } = useKibana(); const createIndexLocator = useMemo( - () => share.url.locators.get('CREATE_INDEX_LOCATOR_ID'), + () => + share.url.locators.get('CREATE_INDEX_LOCATOR_ID') ?? + share.url.locators.get('SEARCH_CREATE_INDEX'), [share.url.locators] ); const handleNavigateToIndex = useCallback(async () => { diff --git a/x-pack/plugins/serverless_search/public/navigation_tree.ts b/x-pack/plugins/serverless_search/public/navigation_tree.ts index b0f5a4658e7d..066ab8e8c093 100644 --- a/x-pack/plugins/serverless_search/public/navigation_tree.ts +++ b/x-pack/plugins/serverless_search/public/navigation_tree.ts @@ -20,14 +20,6 @@ export const navigationTree = (): NavigationTreeDefinition => ({ isCollapsible: false, breadcrumbStatus: 'hidden', children: [ - { - id: 'home', - title: i18n.translate('xpack.serverlessSearch.nav.home', { - defaultMessage: 'Home', - }), - link: 'elasticsearchStart', - spaceBefore: 'm', - }, { id: 'data', title: i18n.translate('xpack.serverlessSearch.nav.data', { @@ -47,9 +39,8 @@ export const navigationTree = (): NavigationTreeDefinition => ({ pathNameSerialized.startsWith( prepend('/app/management/data/index_management/') ) || - pathNameSerialized.startsWith( - prepend('/app/elasticsearch/indices/index_details/') - ) + pathNameSerialized.startsWith(prepend('/app/elasticsearch/indices')) || + pathNameSerialized.startsWith(prepend('/app/elasticsearch/start')) ); }, }, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 0008cac9a55d..9d8958957421 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -36088,32 +36088,31 @@ "xpack.searchIndices.server.createIndex.errorMessage": "Échec de la création de l'index en raison d'une exception. {errorMessage}", "xpack.searchIndices.server.deleteDocument.errorMessage": "Impossible de supprimer le document", "xpack.searchIndices.settingsTabLabel": "Paramètres", - "xpack.searchIndices.startPage.codeView.apiKeyDescription": "Assurez-vous de le conserver dans un endroit sûr. Vous ne pourrez pas le récupérer plus tard.", - "xpack.searchIndices.startPage.codeView.apiKeyTitle": "Copier votre clé d'API", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyDescription": "Créez une clé d'API pour vous connecter à Elasticsearch.", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyTitle": "Créer une clé d'API", - "xpack.searchIndices.startPage.createIndex.action.text": "Créer mon index", - "xpack.searchIndices.startPage.createIndex.apiKeyCreation.description": "Nous allons créer une clé d'API pour cet index", - "xpack.searchIndices.startPage.createIndex.description": "Un index stocke vos données et définit le schéma, ou les mappings de champs, pour vos recherches", - "xpack.searchIndices.startPage.createIndex.fileUpload.link": "Charger un fichier", - "xpack.searchIndices.startPage.createIndex.fileUpload.text": "Vous disposez déjà de données ? {link}", - "xpack.searchIndices.startPage.createIndex.name.helpText": "Les noms d'index doivent être en minuscules et ne peuvent contenir que des tirets et des chiffres", - "xpack.searchIndices.startPage.createIndex.name.label": "Nommer votre index", - "xpack.searchIndices.startPage.createIndex.name.placeholder": "Définir un nom pour votre index", - "xpack.searchIndices.startPage.createIndex.permissionTooltip": "Vous ne disposez pas d'autorisation pour créer un index.", + "xpack.searchIndices.shared.codeView.apiKeyDescription": "Assurez-vous de le conserver dans un endroit sûr. Vous ne pourrez pas le récupérer plus tard.", + "xpack.searchIndices.shared.codeView.apiKeyTitle": "Copier votre clé d'API", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyDescription": "Créez une clé d'API pour vous connecter à Elasticsearch.", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyTitle": "Créer une clé d'API", + "xpack.searchIndices.shared.createIndex.action.text": "Créer mon index", + "xpack.searchIndices.shared.createIndex.apiKeyCreation.description": "Nous allons créer une clé d'API pour cet index", + "xpack.searchIndices.shared.createIndex.description": "Un index stocke vos données et définit le schéma, ou les mappings de champs, pour vos recherches", + "xpack.searchIndices.shared.createIndex.fileUpload.link": "Charger un fichier", + "xpack.searchIndices.shared.createIndex.fileUpload.text": "Vous disposez déjà de données ? {link}", + "xpack.searchIndices.shared.createIndex.name.helpText": "Les noms d'index doivent être en minuscules et ne peuvent contenir que des tirets et des chiffres", + "xpack.searchIndices.shared.createIndex.name.label": "Nommer votre index", + "xpack.searchIndices.shared.createIndex.name.placeholder": "Définir un nom pour votre index", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.button": "Collectez et analysez les logs", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.subTitle": "Explorer Logstash et Beats", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.button": "Démarrer un essai d'Observability", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.subTitle": "Puissant monitoring des performances", + "xpack.searchIndices.shared.createIndex.observabilityCallout.title": "Vous cherchez à stocker vos logs ou vos données d'indicateurs ?", + "xpack.searchIndices.shared.createIndex.pageTitle": "Elasticsearch", + "xpack.searchIndices.shared.createIndex.permissionTooltip": "Vous ne disposez pas d'autorisation pour créer un index.", + "xpack.searchIndices.shared.createIndex.viewSelect.code": "Code", + "xpack.searchIndices.shared.createIndex.viewSelect.legend": "Créer une sélection de vue d'index", + "xpack.searchIndices.shared.createIndex.viewSelect.ui": "Interface utilisateur", + "xpack.searchIndices.shared.statusFetchError.title": "Erreur lors du chargement des index", + "xpack.searchIndices.shared.statusFetchError.unknownError": "Erreur inconnue lors de la récupération des index.", "xpack.searchIndices.startPage.createIndex.title": "Créer votre premier index", - "xpack.searchIndices.startPage.createIndex.viewSelec.legend": "Créer une sélection de vue d'index", - "xpack.searchIndices.startPage.createIndex.viewSelect.code": "Code", - "xpack.searchIndices.startPage.createIndex.viewSelect.ui": "Interface utilisateur", - "xpack.searchIndices.startPage.observabilityCallout.logs.button": "Collectez et analysez les logs", - "xpack.searchIndices.startPage.observabilityCallout.logs.subTitle": "Explorer Logstash et Beats", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.button": "Démarrer un essai d'Observability", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.subTitle": "Puissant monitoring des performances", - "xpack.searchIndices.startPage.observabilityCallout.title": "Vous cherchez à stocker vos logs ou vos données d'indicateurs ?", - "xpack.searchIndices.startPage.pageDescription": "Vectorisez, recherchez et visualisez vos données", - "xpack.searchIndices.startPage.pageTitle": "Elasticsearch", - "xpack.searchIndices.startPage.statusFetchError.title": "Erreur lors du chargement des index", - "xpack.searchIndices.startPage.statusFetchError.unknownError": "Erreur inconnue lors de la récupération des index.", "xpack.searchInferenceEndpoints.actions.copyID": "Copier l'identifiant du point de terminaison d'inférence {inferenceId}", "xpack.searchInferenceEndpoints.actions.copyIDSuccess": "Identifiant du point de terminaison d'inférence {inferenceId} copié", "xpack.searchInferenceEndpoints.actions.deleteEndpoint": "Supprimer le point de terminaison d'inférence {selectedEndpointName}", @@ -43280,7 +43279,6 @@ "xpack.serverlessSearch.nav.content.indices": "Gestion des index", "xpack.serverlessSearch.nav.devTools": "Outils de développement", "xpack.serverlessSearch.nav.gettingStarted": "Commencer", - "xpack.serverlessSearch.nav.home": "Accueil", "xpack.serverlessSearch.nav.mngt": "Gestion", "xpack.serverlessSearch.nav.performance": "Performances", "xpack.serverlessSearch.nav.projectSettings": "Paramètres de projet", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1d90ec5e84fd..c923e7c19a85 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -36056,32 +36056,31 @@ "xpack.searchIndices.server.createIndex.errorMessage": "例外が発生したため、インデックスを作成できませんでした。{errorMessage}", "xpack.searchIndices.server.deleteDocument.errorMessage": "ドキュメントを削除できませんでした", "xpack.searchIndices.settingsTabLabel": "設定", - "xpack.searchIndices.startPage.codeView.apiKeyDescription": "必ず安全に保管してください。後から取得することはできません。", - "xpack.searchIndices.startPage.codeView.apiKeyTitle": "APIキーをコピー", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyDescription": "Elasticsearchに接続するためのAPIキーを作成します。", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyTitle": "APIキーを作成する", - "xpack.searchIndices.startPage.createIndex.action.text": "インデックスを作成", - "xpack.searchIndices.startPage.createIndex.apiKeyCreation.description": "このインデックスのAPIキーを作成します", - "xpack.searchIndices.startPage.createIndex.description": "インデックスはデータを格納し、検索のためのスキーマ、つまりフィールドマッピングを定義します。", - "xpack.searchIndices.startPage.createIndex.fileUpload.link": "ファイルをアップロード", - "xpack.searchIndices.startPage.createIndex.fileUpload.text": "すでに一部のデータがありますか?{link}", - "xpack.searchIndices.startPage.createIndex.name.helpText": "インデックス名は小文字で、ハイフンと数字のみを使用する必要があります。", - "xpack.searchIndices.startPage.createIndex.name.label": "インデックスの名前を指定", - "xpack.searchIndices.startPage.createIndex.name.placeholder": "インデックスの名前を入力", - "xpack.searchIndices.startPage.createIndex.permissionTooltip": "APIキーを作成する権限がありません。", + "xpack.searchIndices.shared.codeView.apiKeyDescription": "必ず安全に保管してください。後から取得することはできません。", + "xpack.searchIndices.shared.codeView.apiKeyTitle": "APIキーをコピー", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyDescription": "Elasticsearchに接続するためのAPIキーを作成します。", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyTitle": "APIキーを作成する", + "xpack.searchIndices.shared.createIndex.action.text": "インデックスを作成", + "xpack.searchIndices.shared.createIndex.apiKeyCreation.description": "このインデックスのAPIキーを作成します", + "xpack.searchIndices.shared.createIndex.description": "インデックスはデータを格納し、検索のためのスキーマ、つまりフィールドマッピングを定義します。", + "xpack.searchIndices.shared.createIndex.fileUpload.link": "ファイルをアップロード", + "xpack.searchIndices.shared.createIndex.fileUpload.text": "すでに一部のデータがありますか?{link}", + "xpack.searchIndices.shared.createIndex.name.helpText": "インデックス名は小文字で、ハイフンと数字のみを使用する必要があります。", + "xpack.searchIndices.shared.createIndex.name.label": "インデックスの名前を指定", + "xpack.searchIndices.shared.createIndex.name.placeholder": "インデックスの名前を入力", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.button": "ログを収集して分析", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.subTitle": "LogstashとBeatsを探索", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.button": "オブザーバビリティの試用を開始", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.subTitle": "強力なパフォーマンス監視", + "xpack.searchIndices.shared.createIndex.observabilityCallout.title": "ログとメトリックデータを格納する方法をお探しですか?", + "xpack.searchIndices.shared.createIndex.pageTitle": "Elasticsearch", + "xpack.searchIndices.shared.createIndex.permissionTooltip": "APIキーを作成する権限がありません。", + "xpack.searchIndices.shared.createIndex.viewSelect.code": "コード", + "xpack.searchIndices.shared.createIndex.viewSelect.legend": "インデックスビュー選択を作成", + "xpack.searchIndices.shared.createIndex.viewSelect.ui": "UI", + "xpack.searchIndices.shared.statusFetchError.title": "インデックスの読み込み中にエラーが発生", + "xpack.searchIndices.shared.statusFetchError.unknownError": "インデックスの取得中の不明なエラー", "xpack.searchIndices.startPage.createIndex.title": "最初のインデックスを作成", - "xpack.searchIndices.startPage.createIndex.viewSelec.legend": "インデックスビュー選択を作成", - "xpack.searchIndices.startPage.createIndex.viewSelect.code": "コード", - "xpack.searchIndices.startPage.createIndex.viewSelect.ui": "UI", - "xpack.searchIndices.startPage.observabilityCallout.logs.button": "ログを収集して分析", - "xpack.searchIndices.startPage.observabilityCallout.logs.subTitle": "LogstashとBeatsを探索", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.button": "オブザーバビリティの試用を開始", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.subTitle": "強力なパフォーマンス監視", - "xpack.searchIndices.startPage.observabilityCallout.title": "ログとメトリックデータを格納する方法をお探しですか?", - "xpack.searchIndices.startPage.pageDescription": "データをベクトル化、検索、可視化", - "xpack.searchIndices.startPage.pageTitle": "Elasticsearch", - "xpack.searchIndices.startPage.statusFetchError.title": "インデックスの読み込み中にエラーが発生", - "xpack.searchIndices.startPage.statusFetchError.unknownError": "インデックスの取得中の不明なエラー", "xpack.searchInferenceEndpoints.actions.copyID": "推論エンドポイント ID {inferenceId}をコピー", "xpack.searchInferenceEndpoints.actions.copyIDSuccess": "推論エンドポイント ID {inferenceId}がコピーされました", "xpack.searchInferenceEndpoints.actions.deleteEndpoint": "推論エンドポイント{selectedEndpointName}を削除", @@ -43245,7 +43244,6 @@ "xpack.serverlessSearch.nav.content.indices": "インデックス管理", "xpack.serverlessSearch.nav.devTools": "開発ツール", "xpack.serverlessSearch.nav.gettingStarted": "はじめに", - "xpack.serverlessSearch.nav.home": "ホーム", "xpack.serverlessSearch.nav.mngt": "管理", "xpack.serverlessSearch.nav.performance": "パフォーマンス", "xpack.serverlessSearch.nav.projectSettings": "プロジェクト設定", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 30eb1f0c0fc2..ad65daedc740 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -35493,32 +35493,30 @@ "xpack.searchIndices.server.createIndex.errorMessage": "由于出现异常,无法创建索引。{errorMessage}", "xpack.searchIndices.server.deleteDocument.errorMessage": "无法删除文档", "xpack.searchIndices.settingsTabLabel": "设置", - "xpack.searchIndices.startPage.codeView.apiKeyDescription": "请确保将其存放在某个安全位置。稍后您将无法对其进行检索。", - "xpack.searchIndices.startPage.codeView.apiKeyTitle": "复制您的 API 密钥", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyDescription": "创建 API 密钥以连接到 Elasticsearch。", - "xpack.searchIndices.startPage.codeView.explicitGenerate.apiKeyTitle": "创建 API 密钥", - "xpack.searchIndices.startPage.createIndex.action.text": "创建我的索引", - "xpack.searchIndices.startPage.createIndex.apiKeyCreation.description": "我们将为此索引创建 API 密钥", - "xpack.searchIndices.startPage.createIndex.description": "索引存储您的数据并为您的搜索定义架构或字段映射", - "xpack.searchIndices.startPage.createIndex.fileUpload.link": "上传文件", - "xpack.searchIndices.startPage.createIndex.fileUpload.text": "已具有某些数据?{link}", - "xpack.searchIndices.startPage.createIndex.name.helpText": "索引名称必须为小写,并且只能包含连字符和数字", - "xpack.searchIndices.startPage.createIndex.name.label": "命名您的索引", - "xpack.searchIndices.startPage.createIndex.name.placeholder": "输入索引的名称", - "xpack.searchIndices.startPage.createIndex.permissionTooltip": "您无权创建索引。", + "xpack.searchIndices.shared.codeView.apiKeyDescription": "请确保将其存放在某个安全位置。稍后您将无法对其进行检索。", + "xpack.searchIndices.shared.codeView.apiKeyTitle": "复制您的 API 密钥", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyDescription": "创建 API 密钥以连接到 Elasticsearch。", + "xpack.searchIndices.shared.codeView.explicitGenerate.apiKeyTitle": "创建 API 密钥", + "xpack.searchIndices.shared.createIndex.action.text": "创建我的索引", + "xpack.searchIndices.shared.createIndex.apiKeyCreation.description": "我们将为此索引创建 API 密钥", + "xpack.searchIndices.shared.createIndex.description": "索引存储您的数据并为您的搜索定义架构或字段映射", + "xpack.searchIndices.shared.createIndex.fileUpload.link": "上传文件", + "xpack.searchIndices.shared.createIndex.fileUpload.text": "已具有某些数据?{link}", + "xpack.searchIndices.shared.createIndex.name.helpText": "索引名称必须为小写,并且只能包含连字符和数字", + "xpack.searchIndices.shared.createIndex.name.label": "命名您的索引", + "xpack.searchIndices.shared.createIndex.name.placeholder": "输入索引的名称", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.button": "收集和分析日志", + "xpack.searchIndices.shared.createIndex.observabilityCallout.logs.subTitle": "浏览 Logstash 和 Beats", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.button": "开始 Observability 试用", + "xpack.searchIndices.shared.createIndex.observabilityCallout.o11yTrial.subTitle": "强大的性能监测", + "xpack.searchIndices.shared.createIndex.observabilityCallout.title": "计划存储您的日志或指标数据?", + "xpack.searchIndices.shared.createIndex.pageTitle": "Elasticsearch", + "xpack.searchIndices.shared.createIndex.permissionTooltip": "您无权创建索引。", + "xpack.searchIndices.shared.createIndex.viewSelect.code": "Code", + "xpack.searchIndices.shared.createIndex.viewSelect.ui": "UI", + "xpack.searchIndices.shared.statusFetchError.title": "加载索引时出错", + "xpack.searchIndices.shared.statusFetchError.unknownError": "提取索引时出现未知错误。", "xpack.searchIndices.startPage.createIndex.title": "创建您的首个索引", - "xpack.searchIndices.startPage.createIndex.viewSelec.legend": "创建索引视图选择", - "xpack.searchIndices.startPage.createIndex.viewSelect.code": "Code", - "xpack.searchIndices.startPage.createIndex.viewSelect.ui": "UI", - "xpack.searchIndices.startPage.observabilityCallout.logs.button": "收集和分析日志", - "xpack.searchIndices.startPage.observabilityCallout.logs.subTitle": "浏览 Logstash 和 Beats", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.button": "开始 Observability 试用", - "xpack.searchIndices.startPage.observabilityCallout.o11yTrial.subTitle": "强大的性能监测", - "xpack.searchIndices.startPage.observabilityCallout.title": "计划存储您的日志或指标数据?", - "xpack.searchIndices.startPage.pageDescription": "向量化、搜索和可视化您的数据", - "xpack.searchIndices.startPage.pageTitle": "Elasticsearch", - "xpack.searchIndices.startPage.statusFetchError.title": "加载索引时出错", - "xpack.searchIndices.startPage.statusFetchError.unknownError": "提取索引时出现未知错误。", "xpack.searchInferenceEndpoints.actions.copyID": "复制推理终端 ID {inferenceId}", "xpack.searchInferenceEndpoints.actions.copyIDSuccess": "已复制推理终端 ID {inferenceId}", "xpack.searchInferenceEndpoints.actions.deleteEndpoint": "删除推理终端 {selectedEndpointName}", @@ -42590,7 +42588,6 @@ "xpack.serverlessSearch.nav.content.indices": "索引管理", "xpack.serverlessSearch.nav.devTools": "开发工具", "xpack.serverlessSearch.nav.gettingStarted": "入门", - "xpack.serverlessSearch.nav.home": "主页", "xpack.serverlessSearch.nav.mngt": "管理", "xpack.serverlessSearch.nav.performance": "性能", "xpack.serverlessSearch.nav.projectSettings": "项目设置", diff --git a/x-pack/test/functional/page_objects/index_management_page.ts b/x-pack/test/functional/page_objects/index_management_page.ts index f257f76cbfc5..8053293f9863 100644 --- a/x-pack/test/functional/page_objects/index_management_page.ts +++ b/x-pack/test/functional/page_objects/index_management_page.ts @@ -162,13 +162,13 @@ export function IndexManagementPageProvider({ getService }: FtrProviderContext) }, async clickCreateIndexButton() { await testSubjects.click('createIndexButton'); - await testSubjects.existOrFail('createIndexSaveButton'); }, async setCreateIndexName(value: string) { await testSubjects.existOrFail('createIndexNameFieldText'); await testSubjects.setValue('createIndexNameFieldText', value); }, async clickCreateIndexSaveButton() { + await testSubjects.existOrFail('createIndexSaveButton'); await testSubjects.click('createIndexSaveButton'); // Wait for modal to close await testSubjects.missingOrFail('createIndexSaveButton', { diff --git a/x-pack/test_serverless/functional/page_objects/index.ts b/x-pack/test_serverless/functional/page_objects/index.ts index 0c6b776433b0..2c894b28b065 100644 --- a/x-pack/test_serverless/functional/page_objects/index.ts +++ b/x-pack/test_serverless/functional/page_objects/index.ts @@ -24,6 +24,7 @@ import { SvlSearchHomePageProvider } from './svl_search_homepage'; import { SvlSearchIndexDetailPageProvider } from './svl_search_index_detail_page'; import { SvlSearchElasticsearchStartPageProvider } from './svl_search_elasticsearch_start_page'; import { SvlApiKeysProvider } from './svl_api_keys'; +import { SvlSearchCreateIndexPageProvider } from './svl_search_create_index_page'; export const pageObjects = { ...xpackFunctionalPageObjects, @@ -45,4 +46,5 @@ export const pageObjects = { svlSearchIndexDetailPage: SvlSearchIndexDetailPageProvider, svlSearchElasticsearchStartPage: SvlSearchElasticsearchStartPageProvider, svlApiKeys: SvlApiKeysProvider, + svlSearchCreateIndexPage: SvlSearchCreateIndexPageProvider, }; diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_create_index_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_create_index_page.ts new file mode 100644 index 000000000000..3d0d48f98a6a --- /dev/null +++ b/x-pack/test_serverless/functional/page_objects/svl_search_create_index_page.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export function SvlSearchCreateIndexPageProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const retry = getService('retry'); + + return { + async expectToBeOnCreateIndexPage() { + expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/create'); + await testSubjects.existOrFail('elasticsearchCreateIndexPage', { timeout: 2000 }); + }, + async expectToBeOnIndexDetailsPage() { + await retry.tryForTime(60 * 1000, async () => { + expect(await browser.getCurrentUrl()).contain('/app/elasticsearch/indices/index_details'); + }); + }, + async expectToBeOnIndexListPage() { + await retry.tryForTime(60 * 1000, async () => { + expect(await browser.getCurrentUrl()).contain( + '/app/management/data/index_management/indices' + ); + }); + }, + async expectToBeOnMLFileUploadPage() { + await retry.tryForTime(60 * 1000, async () => { + expect(await browser.getCurrentUrl()).contain('/app/ml/filedatavisualizer'); + }); + }, + async expectIndexNameToExist() { + await testSubjects.existOrFail('indexNameField'); + }, + async setIndexNameValue(value: string) { + await testSubjects.existOrFail('indexNameField'); + await testSubjects.setValue('indexNameField', value); + }, + async expectCloseCreateIndexButtonExists() { + await testSubjects.existOrFail('closeCreateIndex'); + }, + async clickCloseCreateIndexButton() { + await testSubjects.existOrFail('closeCreateIndex'); + await testSubjects.click('closeCreateIndex'); + }, + async expectCreateIndexButtonToExist() { + await testSubjects.existOrFail('createIndexBtn'); + }, + async expectCreateIndexButtonToBeEnabled() { + await testSubjects.existOrFail('createIndexBtn'); + expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); + }, + async expectCreateIndexButtonToBeDisabled() { + await testSubjects.existOrFail('createIndexBtn'); + expect(await testSubjects.isEnabled('createIndexBtn')).equal(false); + }, + async clickCreateIndexButton() { + await testSubjects.existOrFail('createIndexBtn'); + expect(await testSubjects.isEnabled('createIndexBtn')).equal(true); + await testSubjects.click('createIndexBtn'); + }, + async expectCreateIndexCodeView() { + await testSubjects.existOrFail('createIndexCodeView'); + }, + async expectCreateIndexUIView() { + await testSubjects.existOrFail('createIndexUIView'); + }, + async clickUIViewButton() { + await testSubjects.existOrFail('createIndexUIViewBtn'); + await testSubjects.click('createIndexUIViewBtn'); + }, + async clickCodeViewButton() { + await testSubjects.existOrFail('createIndexCodeViewBtn'); + await testSubjects.click('createIndexCodeViewBtn'); + }, + async clickFileUploadLink() { + await testSubjects.existOrFail('uploadFileLink'); + await testSubjects.click('uploadFileLink'); + }, + async expectAPIKeyVisibleInCodeBlock(apiKey: string) { + await testSubjects.existOrFail('createIndex-code-block'); + await retry.try(async () => { + expect(await testSubjects.getVisibleText('createIndex-code-block')).to.contain(apiKey); + }); + }, + + async expectAPIKeyPreGenerated() { + await testSubjects.existOrFail('apiKeyHasBeenGenerated'); + }, + + async expectAPIKeyNotPreGenerated() { + await testSubjects.existOrFail('apiKeyHasNotBeenGenerated'); + }, + + async expectAPIKeyFormNotAvailable() { + await testSubjects.missingOrFail('apiKeyHasNotBeenGenerated'); + await testSubjects.missingOrFail('apiKeyHasBeenGenerated'); + }, + }; +} diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts index 3a3c9700cf2a..aadb41d6f432 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_elasticsearch_start_page.ts @@ -42,6 +42,20 @@ export function SvlSearchElasticsearchStartPageProvider({ getService }: FtrProvi await testSubjects.existOrFail('indexNameField'); await testSubjects.setValue('indexNameField', value); }, + async expectCloseCreateIndexButtonExists() { + await testSubjects.existOrFail('closeCreateIndex'); + }, + async clickCloseCreateIndexButton() { + await testSubjects.existOrFail('closeCreateIndex'); + await testSubjects.click('closeCreateIndex'); + }, + async expectSkipButtonExists() { + await testSubjects.existOrFail('createIndexSkipBtn'); + }, + async clickSkipButton() { + await testSubjects.existOrFail('createIndexSkipBtn'); + await testSubjects.click('createIndexSkipBtn'); + }, async expectCreateIndexButtonToExist() { await testSubjects.existOrFail('createIndexBtn'); }, diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts index f8734e610a61..be3b683d9903 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts @@ -14,6 +14,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const security = getService('security'); const testIndexName = `index-ftr-test-${Math.random()}`; describe('Index Details ', function () { + this.tags(['skipSvlSearch']); before(async () => { await security.testUser.setRoles(['index_management_user']); await pageObjects.svlCommonPage.loginAsAdmin(); @@ -34,7 +35,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.indexManagement.expectIndexToExist(testIndexName); }); describe('can view index details', function () { - this.tags(['skipSvlSearch']); it('index with no documents', async () => { await pageObjects.indexManagement.indexDetailsPage.openIndexDetailsPage(0); await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts index e98fcc09e97d..0d8f09112362 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/indices.ts @@ -17,6 +17,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const es = getService('es'); describe('Indices', function () { + this.tags(['skipSvlSearch']); before(async () => { await security.testUser.setRoles(['index_management_user']); await pageObjects.svlCommonPage.loginAsAdmin(); @@ -53,7 +54,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { after(async () => { await esDeleteAllIndices(testIndexName); }); - this.tags('skipSvlSearch'); it('navigates to overview', async () => { await pageObjects.indexManagement.changeManageIndexTab('showOverviewIndexMenuButton'); await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); diff --git a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts index 129f769283b3..39228137cf7d 100644 --- a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts +++ b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts @@ -156,6 +156,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlSearchElasticsearchStartPage.expectAnalyzeLogsLink(); await pageObjects.svlSearchElasticsearchStartPage.expectO11yTrialLink(); }); + + it('should have close button', async () => { + await pageObjects.svlSearchElasticsearchStartPage.expectToBeOnStartPage(); + await pageObjects.svlSearchElasticsearchStartPage.expectCloseCreateIndexButtonExists(); + await pageObjects.svlSearchElasticsearchStartPage.clickCloseCreateIndexButton(); + await pageObjects.svlSearchElasticsearchStartPage.expectToBeOnIndexListPage(); + }); + it('should have skip button', async () => { + await pageObjects.svlSearchElasticsearchStartPage.expectToBeOnStartPage(); + await pageObjects.svlSearchElasticsearchStartPage.expectSkipButtonExists(); + await pageObjects.svlSearchElasticsearchStartPage.clickSkipButton(); + await pageObjects.svlSearchElasticsearchStartPage.expectToBeOnIndexListPage(); + }); }); describe('viewer', function () { before(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/search/index.ts b/x-pack/test_serverless/functional/test_suites/search/index.ts index 903f98c63b77..99190ae0cc07 100644 --- a/x-pack/test_serverless/functional/test_suites/search/index.ts +++ b/x-pack/test_serverless/functional/test_suites/search/index.ts @@ -15,6 +15,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./elasticsearch_start.ts')); loadTestFile(require.resolve('./search_index_detail.ts')); loadTestFile(require.resolve('./getting_started')); + loadTestFile(require.resolve('./index_management')); loadTestFile(require.resolve('./connectors/connectors_overview')); loadTestFile(require.resolve('./default_dataview')); loadTestFile(require.resolve('./pipelines')); diff --git a/x-pack/test_serverless/functional/test_suites/search/index_management.ts b/x-pack/test_serverless/functional/test_suites/search/index_management.ts index ed7f09eecb0e..08b093f66064 100644 --- a/x-pack/test_serverless/functional/test_suites/search/index_management.ts +++ b/x-pack/test_serverless/functional/test_suites/search/index_management.ts @@ -5,6 +5,7 @@ * 2.0. */ +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; import { testHasEmbeddedConsole } from './embedded_console'; @@ -16,11 +17,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'common', 'header', 'indexManagement', + 'svlSearchCreateIndexPage', ]); + const browser = getService('browser'); const security = getService('security'); + const es = getService('es'); const esDeleteAllIndices = getService('esDeleteAllIndices'); const testIndexName = `test-index-ftr-${Math.random()}`; + const testAPIIndexName = `test-api-index-ftr-${Math.random()}`; describe('index management', function () { before(async () => { await security.testUser.setRoles(['index_management_user']); @@ -32,23 +37,81 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.header.waitUntilLoadingHasFinished(); }); after(async () => { - await esDeleteAllIndices(testIndexName); + await esDeleteAllIndices([testIndexName, testAPIIndexName]); + }); + + it('renders the indices tab', async () => { + const url = await browser.getCurrentUrl(); + expect(url).to.contain(`/indices`); }); it('has embedded dev console', async () => { await testHasEmbeddedConsole(pageObjects); }); - it('can create an index', async () => { - await pageObjects.indexManagement.clickCreateIndexButton(); - await pageObjects.indexManagement.setCreateIndexName(testIndexName); - await pageObjects.indexManagement.clickCreateIndexSaveButton(); - await pageObjects.indexManagement.expectIndexToExist(testIndexName); + describe('create index', function () { + beforeEach(async () => { + await pageObjects.common.navigateToApp('indexManagement'); + // Navigate to the indices tab + await pageObjects.indexManagement.changeTabs('indicesTab'); + await pageObjects.header.waitUntilLoadingHasFinished(); + }); + it('can create an index', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); + await pageObjects.svlSearchCreateIndexPage.expectCreateIndexUIView(); + await pageObjects.svlSearchCreateIndexPage.expectCreateIndexButtonToBeEnabled(); + await pageObjects.svlSearchCreateIndexPage.setIndexNameValue(testIndexName); + await pageObjects.svlSearchCreateIndexPage.clickCreateIndexButton(); + await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexDetailsPage(); + await pageObjects.common.navigateToApp('indexManagement'); + await pageObjects.indexManagement.changeTabs('indicesTab'); + await pageObjects.indexManagement.expectIndexToExist(testIndexName); + }); + it('should redirect to index details when index is created via API and on the code view', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + + await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); + await pageObjects.svlSearchCreateIndexPage.expectCreateIndexUIView(); + await pageObjects.svlSearchCreateIndexPage.clickCodeViewButton(); + await pageObjects.svlSearchCreateIndexPage.expectCreateIndexCodeView(); + await es.indices.create({ index: testAPIIndexName }); + await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexDetailsPage(); + }); + it('should have file upload link', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + + await pageObjects.svlSearchCreateIndexPage.expectToBeOnCreateIndexPage(); + await pageObjects.svlSearchCreateIndexPage.clickFileUploadLink(); + await pageObjects.svlSearchCreateIndexPage.expectToBeOnMLFileUploadPage(); + }); + it('should support closing create index page', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + + await pageObjects.svlSearchCreateIndexPage.expectCloseCreateIndexButtonExists(); + await pageObjects.svlSearchCreateIndexPage.clickCloseCreateIndexButton(); + await pageObjects.svlSearchCreateIndexPage.expectToBeOnIndexListPage(); + }); + it('should have the embedded console', async () => { + await pageObjects.indexManagement.clickCreateIndexButton(); + + await testHasEmbeddedConsole(pageObjects); + }); }); - it('can view index details - index with no documents', async () => { - await pageObjects.indexManagement.indexDetailsPage.openIndexDetailsPage(0); - await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); + describe('manage index', function () { + beforeEach(async () => { + await pageObjects.common.navigateToApp('indexManagement'); + // Navigate to the indices tab + await pageObjects.indexManagement.changeTabs('indicesTab'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await pageObjects.indexManagement.manageIndex(testIndexName); + await pageObjects.indexManagement.manageIndexContextMenuExists(); + }); + it('can delete index', async () => { + await pageObjects.indexManagement.confirmDeleteModalIsVisible(); + await pageObjects.indexManagement.expectIndexIsDeleted(testIndexName); + }); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/search/navigation.ts b/x-pack/test_serverless/functional/test_suites/search/navigation.ts index a6da7b1467e9..97952d68f8fd 100644 --- a/x-pack/test_serverless/functional/test_suites/search/navigation.ts +++ b/x-pack/test_serverless/functional/test_suites/search/navigation.ts @@ -36,10 +36,11 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { // check side nav links await solutionNavigation.sidenav.expectSectionExists('search_project_nav'); await solutionNavigation.sidenav.expectLinkActive({ - deepLinkId: 'elasticsearchStart', + deepLinkId: 'management:index_management', }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Indices' }); await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ - deepLinkId: 'elasticsearchStart', + text: 'Create your first index', }); await testSubjects.existOrFail(`elasticsearchStartPage`); @@ -53,6 +54,7 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { }); await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Data' }); await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Index Management' }); + await solutionNavigation.breadcrumbs.expectBreadcrumbExists({ text: 'Indices' }); // > Connectors await solutionNavigation.sidenav.clickLink({ @@ -176,9 +178,9 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { // navigate back to serverless search overview await svlCommonNavigation.clickLogo(); await svlCommonNavigation.sidenav.expectLinkActive({ - deepLinkId: 'elasticsearchStart', + deepLinkId: 'management:index_management', }); - await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ text: `Home` }); + await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ text: `Indices` }); await testSubjects.existOrFail(`elasticsearchStartPage`); await expectNoPageReload(); @@ -236,7 +238,6 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { it('renders expected side navigation items', async () => { await solutionNavigation.sidenav.openSection('project_settings_project_nav'); // Verify all expected top-level links exist - await solutionNavigation.sidenav.expectLinkExists({ text: 'Home' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Data' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Index Management' }); await solutionNavigation.sidenav.expectLinkExists({ text: 'Connectors' }); @@ -261,7 +262,6 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await solutionNavigation.sidenav.openSection('project_settings_project_nav'); await solutionNavigation.sidenav.expectOnlyDefinedLinks([ 'search_project_nav', - 'home', 'data', 'management:index_management', 'serverlessConnectors', diff --git a/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts b/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts index e1570d219137..946afe08a0b7 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_playground/playground_overview.ts @@ -69,7 +69,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToExist(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundHeaderComponentsToDisabled(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); - await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageIndexCalloutExists(); + await pageObjects.searchPlayground.PlaygroundStartChatPage.expectPlaygroundStartChatPageIndexButtonExists(); }); describe('with gen ai connectors', () => { @@ -106,7 +106,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('without any indices', () => { - it('hide no index callout when index added', async () => { + it('hide no create index button when index added', async () => { await createIndex(); await pageObjects.searchPlayground.PlaygroundStartChatPage.expectOpenFlyoutAndSelectIndex(); }); From 160e626ab58bda7cfe442dbb276744f878eaaf90 Mon Sep 17 00:00:00 2001 From: Kfir Peled <61654899+kfirpeled@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:47:23 +0000 Subject: [PATCH 022/100] [Cloud Security] Added filter support to graph API (#199048) ## Summary Enhances the graph API to support filtering by bool query. Graph API is an internal API that hasn't been released yet to ESS, and is not available yet on serverless (behind a feature-flag in kibana.config) due to the above I don't consider it a breaking change. Previous API request body: ```js query: schema.object({ actorIds: schema.arrayOf(schema.string()), eventIds: schema.arrayOf(schema.string()), // TODO: use zod for range validation instead of config schema start: schema.oneOf([schema.number(), schema.string()]), end: schema.oneOf([schema.number(), schema.string()]), ``` New API request body: ```js nodesLimit: schema.maybe(schema.number()), // Maximum number of nodes in the graph (currently the graph doesn't handle very well graph with over 100 nodes) showUnknownTarget: schema.maybe(schema.boolean()), // Whether or not to return events that miss target.entity.id query: schema.object({ eventIds: schema.arrayOf(schema.string()), // Event ids that triggered the alert, would be marked in red // TODO: use zod for range validation instead of config schema start: schema.oneOf([schema.number(), schema.string()]), end: schema.oneOf([schema.number(), schema.string()]), esQuery: schema.maybe( // elasticsearch's dsl bool query schema.object({ bool: schema.object({ filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), must_not: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), }), }) ``` New field to the graph API response (pseudo): ```js messages?: ApiMessageCode[] enum ApiMessageCode { ReachedNodesLimit = 'REACHED_NODES_LIMIT', } ``` ### How to test Toggle feature flag in kibana.dev.yml ```yaml xpack.securitySolution.enableExperimental: ['graphVisualizationInFlyoutEnabled'] ``` To test through the UI you can use the mocked data ```bash node scripts/es_archiver load x-pack/test/cloud_security_posture_functional/es_archives/logs_gcp_audit \ --es-url http://elastic:changeme@localhost:9200 \ --kibana-url http://elastic:changeme@localhost:5601 node scripts/es_archiver load x-pack/test/cloud_security_posture_functional/es_archives/security_alerts \ --es-url http://elastic:changeme@localhost:9200 \ --kibana-url http://elastic:changeme@localhost:5601 ``` 1. Go to the alerts page 2. Change the query time range to show alerts from the 13th of October 2024 (**IMPORTANT**) 3. Open the alerts flyout 5. Scroll to see the graph visualization : D To test **only** the API you can use the mocked data ```bash node scripts/es_archiver load x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit \ --es-url http://elastic:changeme@localhost:9200 \ --kibana-url http://elastic:changeme@localhost:5601 ``` And through dev tools: ``` POST kbn:/internal/cloud_security_posture/graph?apiVersion=1 { "query": { "eventIds": [], "start": "now-1y/y", "end": "now/d", "esQuery": { "bool": { "filter": [ { "match_phrase": { "actor.entity.id": "admin@example.com" } } ] } } } } ``` ### Related PRs - https://github.com/elastic/kibana/pull/196034 - https://github.com/elastic/kibana/pull/195307 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/schema/graph/v1.ts | 17 +- .../common/tsconfig.json | 1 + .../common/types/graph/v1.ts | 13 +- .../server/routes/graph/route.ts | 20 +- .../server/routes/graph/types.ts | 23 -- .../server/routes/graph/v1.ts | 273 +++++++++++------- .../components/graph_preview_container.tsx | 1 - .../right/hooks/use_fetch_graph_data.test.tsx | 89 ++++++ .../right/hooks/use_fetch_graph_data.ts | 19 +- .../es_archives/logs_gcp_audit/data.json | 120 ++++++++ .../routes/graph.ts | 272 +++++++++++++++-- .../test/cloud_security_posture_api/utils.ts | 9 +- .../security/cloud_security_posture/graph.ts | 12 +- 13 files changed, 693 insertions(+), 176 deletions(-) delete mode 100644 x-pack/plugins/cloud_security_posture/server/routes/graph/types.ts create mode 100644 x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.test.tsx diff --git a/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts index 3d37331b4cc5..076c685aca5b 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/schema/graph/v1.ts @@ -6,14 +6,26 @@ */ import { schema } from '@kbn/config-schema'; +import { ApiMessageCode } from '../../types/graph/v1'; export const graphRequestSchema = schema.object({ + nodesLimit: schema.maybe(schema.number()), + showUnknownTarget: schema.maybe(schema.boolean()), query: schema.object({ - actorIds: schema.arrayOf(schema.string()), eventIds: schema.arrayOf(schema.string()), // TODO: use zod for range validation instead of config schema start: schema.oneOf([schema.number(), schema.string()]), end: schema.oneOf([schema.number(), schema.string()]), + esQuery: schema.maybe( + schema.object({ + bool: schema.object({ + filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + must_not: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + }), + }) + ), }), }); @@ -23,6 +35,9 @@ export const graphResponseSchema = () => schema.oneOf([entityNodeDataSchema, groupNodeDataSchema, labelNodeDataSchema]) ), edges: schema.arrayOf(edgeDataSchema), + messages: schema.maybe( + schema.arrayOf(schema.oneOf([schema.literal(ApiMessageCode.ReachedNodesLimit)])) + ), }); export const colorSchema = schema.oneOf([ diff --git a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json index c7cf1e9208bf..ebec9929559f 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json +++ b/x-pack/packages/kbn-cloud-security-posture/common/tsconfig.json @@ -20,5 +20,6 @@ "@kbn/i18n", "@kbn/analytics", "@kbn/usage-collection-plugin", + "@kbn/es-query", ] } diff --git a/x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts b/x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts index 48d1d1c49fd0..f97d11b34732 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/types/graph/v1.ts @@ -6,6 +6,7 @@ */ import type { TypeOf } from '@kbn/config-schema'; +import type { BoolQuery } from '@kbn/es-query'; import { colorSchema, edgeDataSchema, @@ -17,13 +18,21 @@ import { nodeShapeSchema, } from '../../schema/graph/v1'; -export type GraphRequest = TypeOf; -export type GraphResponse = TypeOf; +export type GraphRequest = Omit, 'query.esQuery'> & { + query: { esQuery?: { bool: Partial } }; +}; +export type GraphResponse = Omit, 'messages'> & { + messages?: ApiMessageCode[]; +}; export type Color = typeof colorSchema.type; export type NodeShape = TypeOf; +export enum ApiMessageCode { + ReachedNodesLimit = 'REACHED_NODES_LIMIT', +} + export type EntityNodeDataModel = TypeOf; export type GroupNodeDataModel = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/graph/route.ts b/x-pack/plugins/cloud_security_posture/server/routes/graph/route.ts index 9e9744b33d94..9fb817b275a0 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/graph/route.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/graph/route.ts @@ -10,6 +10,7 @@ import { graphResponseSchema, } from '@kbn/cloud-security-posture-common/schema/graph/latest'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { GraphRequest } from '@kbn/cloud-security-posture-common/types/graph/v1'; import { GRAPH_ROUTE_PATH } from '../../../common/constants'; import { CspRouter } from '../../types'; import { getGraph as getGraphV1 } from './v1'; @@ -39,26 +40,29 @@ export const defineGraphRoute = (router: CspRouter) => }, }, async (context, request, response) => { - const { actorIds, eventIds, start, end } = request.body.query; + const { nodesLimit, showUnknownTarget = false } = request.body; + const { eventIds, start, end, esQuery } = request.body.query as GraphRequest['query']; const cspContext = await context.csp; const spaceId = (await cspContext.spaces?.spacesService?.getActiveSpace(request))?.id; try { - const { nodes, edges } = await getGraphV1( - { + const resp = await getGraphV1({ + services: { logger: cspContext.logger, esClient: cspContext.esClient, }, - { - actorIds, + query: { eventIds, spaceId, start, end, - } - ); + esQuery, + }, + showUnknownTarget, + nodesLimit, + }); - return response.ok({ body: { nodes, edges } }); + return response.ok({ body: resp }); } catch (err) { const error = transformError(err); cspContext.logger.error(`Failed to fetch graph ${err}`); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/graph/types.ts b/x-pack/plugins/cloud_security_posture/server/routes/graph/types.ts deleted file mode 100644 index ba32664da623..000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/graph/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - EdgeDataModel, - NodeDataModel, -} from '@kbn/cloud-security-posture-common/types/graph/latest'; -import type { Logger, IScopedClusterClient } from '@kbn/core/server'; -import type { Writable } from '@kbn/utility-types'; - -export interface GraphContextServices { - logger: Logger; - esClient: IScopedClusterClient; -} - -export interface GraphContext { - nodes: Array>; - edges: Array>; -} diff --git a/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts index 5102d153c190..b14a2ba3e06a 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/graph/v1.ts @@ -8,22 +8,27 @@ import { castArray } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import type { Logger, IScopedClusterClient } from '@kbn/core/server'; +import { ApiMessageCode } from '@kbn/cloud-security-posture-common/types/graph/latest'; import type { + Color, EdgeDataModel, - NodeDataModel, EntityNodeDataModel, - LabelNodeDataModel, + GraphRequest, + GraphResponse, GroupNodeDataModel, -} from '@kbn/cloud-security-posture-common/types/graph/latest'; + LabelNodeDataModel, + NodeDataModel, +} from '@kbn/cloud-security-posture-common/types/graph/v1'; import type { EsqlToRecords } from '@elastic/elasticsearch/lib/helpers'; import type { Writable } from '@kbn/utility-types'; -import type { GraphContextServices, GraphContext } from './types'; + +type EsQuery = GraphRequest['query']['esQuery']; interface GraphEdge { badge: number; - ips: string[]; - hosts: string[]; - users: string[]; + ips?: string[] | string; + hosts?: string[] | string; + users?: string[] | string; actorIds: string[] | string; action: string; targetIds: string[] | string; @@ -36,50 +41,75 @@ interface LabelEdges { target: string; } -export const getGraph = async ( - services: GraphContextServices, +interface GraphContextServices { + logger: Logger; + esClient: IScopedClusterClient; +} + +interface GetGraphParams { + services: GraphContextServices; query: { - actorIds: string[]; eventIds: string[]; spaceId?: string; start: string | number; end: string | number; - } -): Promise<{ - nodes: NodeDataModel[]; - edges: EdgeDataModel[]; -}> => { - const { esClient, logger } = services; - const { actorIds, eventIds, spaceId = 'default', start, end } = query; - - logger.trace( - `Fetching graph for [eventIds: ${eventIds.join(', ')}] [actorIds: ${actorIds.join( - ', ' - )}] in [spaceId: ${spaceId}]` - ); + esQuery?: EsQuery; + }; + showUnknownTarget: boolean; + nodesLimit?: number; +} - const results = await fetchGraph({ esClient, logger, start, end, eventIds, actorIds }); +export const getGraph = async ({ + services: { esClient, logger }, + query: { eventIds, spaceId = 'default', start, end, esQuery }, + showUnknownTarget, + nodesLimit, +}: GetGraphParams): Promise> => { + logger.trace(`Fetching graph for [eventIds: ${eventIds.join(', ')}] in [spaceId: ${spaceId}]`); + + const results = await fetchGraph({ + esClient, + showUnknownTarget, + logger, + start, + end, + eventIds, + esQuery, + }); // Convert results into set of nodes and edges - const graphContext = parseRecords(logger, results.records); - - return { nodes: graphContext.nodes, edges: graphContext.edges }; + return parseRecords(logger, results.records, nodesLimit); }; interface ParseContext { - nodesMap: Record; - edgesMap: Record; - edgeLabelsNodes: Record; - labelEdges: Record; + readonly nodesLimit?: number; + readonly nodesMap: Record; + readonly edgesMap: Record; + readonly edgeLabelsNodes: Record; + readonly labelEdges: Record; + readonly messages: ApiMessageCode[]; + readonly logger: Logger; } -const parseRecords = (logger: Logger, records: GraphEdge[]): GraphContext => { - const ctx: ParseContext = { nodesMap: {}, edgeLabelsNodes: {}, edgesMap: {}, labelEdges: {} }; +const parseRecords = ( + logger: Logger, + records: GraphEdge[], + nodesLimit?: number +): Pick => { + const ctx: ParseContext = { + nodesLimit, + logger, + nodesMap: {}, + edgeLabelsNodes: {}, + edgesMap: {}, + labelEdges: {}, + messages: [], + }; - logger.trace(`Parsing records [length: ${records.length}]`); + logger.trace(`Parsing records [length: ${records.length}] [nodesLimit: ${nodesLimit ?? 'none'}]`); - createNodes(logger, records, ctx); - createEdgesAndGroups(logger, ctx); + createNodes(records, ctx); + createEdgesAndGroups(ctx); logger.trace( `Parsed [nodes: ${Object.keys(ctx.nodesMap).length}, edges: ${ @@ -90,7 +120,11 @@ const parseRecords = (logger: Logger, records: GraphEdge[]): GraphContext => { // Sort groups to be first (fixes minor layout issue) const nodes = sortNodes(ctx.nodesMap); - return { nodes, edges: Object.values(ctx.edgesMap) }; + return { + nodes, + edges: Object.values(ctx.edgesMap), + messages: ctx.messages.length > 0 ? ctx.messages : undefined, + }; }; const fetchGraph = async ({ @@ -98,15 +132,17 @@ const fetchGraph = async ({ logger, start, end, - actorIds, eventIds, + showUnknownTarget, + esQuery, }: { esClient: IScopedClusterClient; logger: Logger; start: string | number; end: string | number; - actorIds: string[]; eventIds: string[]; + showUnknownTarget: boolean; + esQuery?: EsQuery; }): Promise> => { const query = `from logs-* | WHERE event.action IS NOT NULL AND actor.entity.id IS NOT NULL @@ -124,59 +160,84 @@ const fetchGraph = async ({ targetIds = target.entity.id, eventOutcome = event.outcome, isAlert -| LIMIT 1000`; +| LIMIT 1000 +| SORT isAlert DESC`; logger.trace(`Executing query [${query}]`); return await esClient.asCurrentUser.helpers .esql({ columnar: false, - filter: { - bool: { - must: [ + filter: buildDslFilter(eventIds, showUnknownTarget, start, end, esQuery), + query, + // @ts-ignore - types are not up to date + params: [...eventIds.map((id, idx) => ({ [`al_id${idx}`]: id }))], + }) + .toRecords(); +}; + +const buildDslFilter = ( + eventIds: string[], + showUnknownTarget: boolean, + start: string | number, + end: string | number, + esQuery?: EsQuery +) => ({ + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: start, + lte: end, + }, + }, + }, + ...(showUnknownTarget + ? [] + : [ { - range: { - '@timestamp': { - gte: start, - lte: end, - }, + exists: { + field: 'target.entity.id', }, }, + ]), + { + bool: { + should: [ + ...(esQuery?.bool.filter?.length || + esQuery?.bool.must?.length || + esQuery?.bool.should?.length || + esQuery?.bool.must_not?.length + ? [esQuery] + : []), { - bool: { - should: [ - { - terms: { - 'event.id': eventIds, - }, - }, - { - terms: { - 'actor.entity.id': actorIds, - }, - }, - ], - minimum_should_match: 1, + terms: { + 'event.id': eventIds, }, }, ], + minimum_should_match: 1, }, }, - query, - // @ts-ignore - types are not up to date - params: [...eventIds.map((id, idx) => ({ [`al_id${idx}`]: id }))], - }) - .toRecords(); -}; + ], + }, +}); -const createNodes = ( - logger: Logger, - records: GraphEdge[], - context: Omit -) => { +const createNodes = (records: GraphEdge[], context: Omit) => { const { nodesMap, edgeLabelsNodes, labelEdges } = context; for (const record of records) { + if (context.nodesLimit !== undefined && Object.keys(nodesMap).length >= context.nodesLimit) { + context.logger.debug( + `Reached nodes limit [limit: ${context.nodesLimit}] [current: ${ + Object.keys(nodesMap).length + }]` + ); + context.messages.push(ApiMessageCode.ReachedNodesLimit); + break; + } + const { ips, hosts, users, actorIds, action, targetIds, isAlert, eventOutcome } = record; const actorIdsArray = castArray(actorIds); const targetIdsArray = castArray(targetIds); @@ -190,12 +251,6 @@ const createNodes = ( } }); - logger.trace( - `Parsing record [actorIds: ${actorIdsArray.join( - ', ' - )}, action: ${action}, targetIds: ${targetIdsArray.join(', ')}]` - ); - // Create entity nodes [...actorIdsArray, ...targetIdsArray].forEach((id) => { if (nodesMap[id] === undefined) { @@ -203,10 +258,13 @@ const createNodes = ( id, label: unknownTargets.includes(id) ? 'Unknown' : undefined, color: isAlert ? 'danger' : 'primary', - ...determineEntityNodeShape(id, ips ?? [], hosts ?? [], users ?? []), + ...determineEntityNodeShape( + id, + castArray(ips ?? []), + castArray(hosts ?? []), + castArray(users ?? []) + ), }; - - logger.trace(`Creating entity node [${id}]`); } }); @@ -226,8 +284,6 @@ const createNodes = ( shape: 'label', }; - logger.trace(`Creating label node [${labelNode.id}]`); - nodesMap[labelNode.id] = labelNode; edgeLabelsNodes[edgeId].push(labelNode.id); labelEdges[labelNode.id] = { source: actorId, target: targetId }; @@ -278,7 +334,7 @@ const sortNodes = (nodesMap: Record) => { return [...groupNodes, ...otherNodes]; }; -const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { +const createEdgesAndGroups = (context: ParseContext) => { const { edgeLabelsNodes, edgesMap, nodesMap, labelEdges } = context; Object.entries(edgeLabelsNodes).forEach(([edgeId, edgeLabelsIds]) => { @@ -287,7 +343,6 @@ const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { const edgeLabelId = edgeLabelsIds[0]; connectEntitiesAndLabelNode( - logger, edgesMap, nodesMap, labelEdges[edgeLabelId].source, @@ -300,44 +355,47 @@ const createEdgesAndGroups = (logger: Logger, context: ParseContext) => { shape: 'group', }; nodesMap[groupNode.id] = groupNode; + let groupEdgesColor: Color = 'primary'; + + edgeLabelsIds.forEach((edgeLabelId) => { + (nodesMap[edgeLabelId] as Writable).parentId = groupNode.id; + connectEntitiesAndLabelNode(edgesMap, nodesMap, groupNode.id, edgeLabelId, groupNode.id); + + if ((nodesMap[edgeLabelId] as LabelNodeDataModel).color === 'danger') { + groupEdgesColor = 'danger'; + } else if ( + (nodesMap[edgeLabelId] as LabelNodeDataModel).color === 'warning' && + groupEdgesColor !== 'danger' + ) { + // Use warning only if there's no danger color + groupEdgesColor = 'warning'; + } + }); connectEntitiesAndLabelNode( - logger, edgesMap, nodesMap, labelEdges[edgeLabelsIds[0]].source, groupNode.id, - labelEdges[edgeLabelsIds[0]].target + labelEdges[edgeLabelsIds[0]].target, + groupEdgesColor ); - - edgeLabelsIds.forEach((edgeLabelId) => { - (nodesMap[edgeLabelId] as Writable).parentId = groupNode.id; - connectEntitiesAndLabelNode( - logger, - edgesMap, - nodesMap, - groupNode.id, - edgeLabelId, - groupNode.id - ); - }); } }); }; const connectEntitiesAndLabelNode = ( - logger: Logger, edgesMap: Record, nodesMap: Record, sourceNodeId: string, labelNodeId: string, - targetNodeId: string + targetNodeId: string, + colorOverride?: Color ) => { [ - connectNodes(nodesMap, sourceNodeId, labelNodeId), - connectNodes(nodesMap, labelNodeId, targetNodeId), + connectNodes(nodesMap, sourceNodeId, labelNodeId, colorOverride), + connectNodes(nodesMap, labelNodeId, targetNodeId, colorOverride), ].forEach((edge) => { - logger.trace(`Connecting nodes [${edge.source} -> ${edge.target}]`); edgesMap[edge.id] = edge; }); }; @@ -345,7 +403,8 @@ const connectEntitiesAndLabelNode = ( const connectNodes = ( nodesMap: Record, sourceNodeId: string, - targetNodeId: string + targetNodeId: string, + colorOverride?: Color ): EdgeDataModel => { const sourceNode = nodesMap[sourceNodeId]; const targetNode = nodesMap[targetNodeId]; @@ -360,6 +419,6 @@ const connectNodes = ( id: `a(${sourceNodeId})-b(${targetNodeId})`, source: sourceNodeId, target: targetNodeId, - color, + color: colorOverride ?? color, }; }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx index be6559336459..af9e8dca1f24 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/graph_preview_container.tsx @@ -32,7 +32,6 @@ export const GraphPreviewContainer: React.FC = () => { const graphFetchQuery = useFetchGraphData({ req: { query: { - actorIds: [], eventIds, start: DEFAULT_FROM, end: DEFAULT_TO, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.test.tsx new file mode 100644 index 000000000000..c22ec0caa82c --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.test.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useFetchGraphData } from './use_fetch_graph_data'; + +const mockUseQuery = jest.fn(); + +jest.mock('@tanstack/react-query', () => { + return { + useQuery: (...args: unknown[]) => mockUseQuery(...args), + }; +}); + +describe('useFetchGraphData', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('Should pass default options when options are not provided', () => { + renderHook(() => { + return useFetchGraphData({ + req: { + query: { + eventIds: [], + start: '2021-09-01T00:00:00.000Z', + end: '2021-09-01T23:59:59.999Z', + }, + }, + }); + }); + + expect(mockUseQuery.mock.calls).toHaveLength(1); + expect(mockUseQuery.mock.calls[0][2]).toEqual({ + enabled: true, + refetchOnWindowFocus: true, + }); + }); + + it('Should should not be enabled when enabled set to false', () => { + renderHook(() => { + return useFetchGraphData({ + req: { + query: { + eventIds: [], + start: '2021-09-01T00:00:00.000Z', + end: '2021-09-01T23:59:59.999Z', + }, + }, + options: { + enabled: false, + }, + }); + }); + + expect(mockUseQuery.mock.calls).toHaveLength(1); + expect(mockUseQuery.mock.calls[0][2]).toEqual({ + enabled: false, + refetchOnWindowFocus: true, + }); + }); + + it('Should should not be refetchOnWindowFocus when refetchOnWindowFocus set to false', () => { + renderHook(() => { + return useFetchGraphData({ + req: { + query: { + eventIds: [], + start: '2021-09-01T00:00:00.000Z', + end: '2021-09-01T23:59:59.999Z', + }, + }, + options: { + refetchOnWindowFocus: false, + }, + }); + }); + + expect(mockUseQuery.mock.calls).toHaveLength(1); + expect(mockUseQuery.mock.calls[0][2]).toEqual({ + enabled: true, + refetchOnWindowFocus: false, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts index 2304cfb8d4fd..9a0e270a9b2e 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_fetch_graph_data.ts @@ -10,6 +10,7 @@ import type { GraphRequest, GraphResponse, } from '@kbn/cloud-security-posture-common/types/graph/latest'; +import { useMemo } from 'react'; import { EVENT_GRAPH_VISUALIZATION_API } from '../../../../../common/constants'; import { useHttp } from '../../../../common/lib/kibana'; @@ -30,6 +31,11 @@ export interface UseFetchGraphDataParams { * Defaults to true. */ enabled?: boolean; + /** + * If true, the query will refetch on window focus. + * Defaults to true. + */ + refetchOnWindowFocus?: boolean; }; } @@ -61,18 +67,25 @@ export const useFetchGraphData = ({ req, options, }: UseFetchGraphDataParams): UseFetchGraphDataResult => { - const { actorIds, eventIds, start, end } = req.query; + const { eventIds, start, end, esQuery } = req.query; const http = useHttp(); + const QUERY_KEY = useMemo( + () => ['useFetchGraphData', eventIds, start, end, esQuery], + [end, esQuery, eventIds, start] + ); const { isLoading, isError, data } = useQuery( - ['useFetchGraphData', actorIds, eventIds, start, end], + QUERY_KEY, () => { return http.post(EVENT_GRAPH_VISUALIZATION_API, { version: '1', body: JSON.stringify(req), }); }, - options + { + enabled: options?.enabled ?? true, + refetchOnWindowFocus: options?.refetchOnWindowFocus ?? true, + } ); return { diff --git a/x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit/data.json b/x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit/data.json index 9f536d0bb6dc..37f7ebdff5fb 100644 --- a/x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit/data.json +++ b/x-pack/test/cloud_security_posture_api/es_archives/logs_gcp_audit/data.json @@ -497,3 +497,123 @@ } } } + +{ + "type": "doc", + "value": { + "data_stream": "logs-gcp.audit-default", + "id": "5", + "index": ".ds-logs-gcp.audit-default-2024.10.07-000001", + "source": { + "@timestamp": "2024-09-01T12:34:56.789Z", + "actor": { + "entity": { + "id": "admin5@example.com" + } + }, + "client": { + "user": { + "email": "admin5@example.com" + } + }, + "cloud": { + "project": { + "id": "your-project-id" + }, + "provider": "gcp" + }, + "ecs": { + "version": "8.11.0" + }, + "event": { + "action": "google.iam.admin.v1.ListRoles", + "agent_id_status": "missing", + "category": [ + "session", + "network", + "configuration" + ], + "id": "without target", + "ingested": "2024-10-07T17:47:35Z", + "kind": "event", + "outcome": "success", + "provider": "activity", + "type": [ + "end", + "access", + "allowed" + ] + }, + "gcp": { + "audit": { + "authorization_info": [ + { + "granted": true, + "permission": "iam.roles.create", + "resource": "projects/your-project-id" + } + ], + "logentry_operation": { + "id": "operation-0987654321" + }, + "request": { + "@type": "type.googleapis.com/google.iam.admin.v1.CreateRoleRequest", + "parent": "projects/your-project-id", + "role": { + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "title": "Custom Role" + }, + "roleId": "customRole" + }, + "resource_name": "projects/your-project-id/roles/customRole", + "response": { + "@type": "type.googleapis.com/google.iam.admin.v1.Role", + "description": "A custom role with specific permissions", + "includedPermissions": [ + "resourcemanager.projects.get", + "resourcemanager.projects.list" + ], + "name": "projects/your-project-id/roles/customRole", + "stage": "GA", + "title": "Custom Role" + }, + "type": "type.googleapis.com/google.cloud.audit.AuditLog" + } + }, + "log": { + "level": "NOTICE", + "logger": "projects/your-project-id/logs/cloudaudit.googleapis.com%2Factivity" + }, + "related": { + "ip": [ + "10.0.0.1" + ], + "user": [ + "admin3@example.com" + ] + }, + "service": { + "name": "iam.googleapis.com" + }, + "source": { + "ip": "10.0.0.1" + }, + "tags": [ + "_geoip_database_unavailable_GeoLite2-City.mmdb", + "_geoip_database_unavailable_GeoLite2-ASN.mmdb" + ], + "user_agent": { + "device": { + "name": "Other" + }, + "name": "Other", + "original": "google-cloud-sdk/324.0.0" + } + } + } +} diff --git a/x-pack/test/cloud_security_posture_api/routes/graph.ts b/x-pack/test/cloud_security_posture_api/routes/graph.ts index bd2f71ef3b9b..8043e6e22feb 100644 --- a/x-pack/test/cloud_security_posture_api/routes/graph.ts +++ b/x-pack/test/cloud_security_posture_api/routes/graph.ts @@ -11,6 +11,8 @@ import { } from '@kbn/core-http-common'; import expect from '@kbn/expect'; import type { Agent } from 'supertest'; +import { ApiMessageCode } from '@kbn/cloud-security-posture-common/types/graph/latest'; +import type { GraphRequest } from '@kbn/cloud-security-posture-common/types/graph/latest'; import { FtrProviderContext } from '../ftr_provider_context'; import { result } from '../utils'; import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; @@ -19,12 +21,13 @@ import { CspSecurityCommonProvider } from './helper/user_roles_utilites'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; + const logger = getService('log'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const cspSecurity = CspSecurityCommonProvider(providerContext); - const postGraph = (agent: Agent, body: any, auth?: { user: string; pass: string }) => { + const postGraph = (agent: Agent, body: GraphRequest, auth?: { user: string; pass: string }) => { const req = agent .post('/internal/cloud_security_posture/graph') .set(ELASTIC_HTTP_VERSION_HEADER, '1') @@ -45,7 +48,6 @@ export default function (providerContext: FtrProviderContext) { supertestWithoutAuth, { query: { - actorIds: [], eventIds: [], start: 'now-1d/d', end: 'now/d', @@ -55,19 +57,7 @@ export default function (providerContext: FtrProviderContext) { user: 'role_security_no_read_user', pass: cspSecurity.getPasswordForUser('role_security_no_read_user'), } - ).expect(result(403)); - }); - }); - - describe('Validation', () => { - it('should return 400 when missing `actorIds` field', async () => { - await postGraph(supertest, { - query: { - eventIds: [], - start: 'now-1d/d', - end: 'now/d', - }, - }).expect(result(400)); + ).expect(result(403, logger)); }); }); @@ -84,10 +74,54 @@ export default function (providerContext: FtrProviderContext) { ); }); - it('should return an empty graph', async () => { + describe('Validation', () => { + it('should return 400 when missing `eventIds` field', async () => { + await postGraph(supertest, { + // @ts-expect-error ignore error for testing + query: { + start: 'now-1d/d', + end: 'now/d', + }, + }).expect(result(400, logger)); + }); + + it('should return 400 when missing `esQuery` field is not of type bool', async () => { + await postGraph(supertest, { + query: { + eventIds: [], + start: 'now-1d/d', + end: 'now/d', + esQuery: { + // @ts-expect-error ignore error for testing + match_all: {}, + }, + }, + }).expect(result(400, logger)); + }); + + it('should return 400 with unsupported `esQuery`', async () => { + await postGraph(supertest, { + query: { + eventIds: [], + start: 'now-1d/d', + end: 'now/d', + esQuery: { + bool: { + filter: [ + { + unsupported: 'unsupported', + }, + ], + }, + }, + }, + }).expect(result(400, logger)); + }); + }); + + it('should return an empty graph / should return 200 when missing `esQuery` field', async () => { const response = await postGraph(supertest, { query: { - actorIds: [], eventIds: [], start: 'now-1d/d', end: 'now/d', @@ -96,20 +130,32 @@ export default function (providerContext: FtrProviderContext) { expect(response.body).to.have.property('nodes').length(0); expect(response.body).to.have.property('edges').length(0); + expect(response.body).not.to.have.property('messages'); }); it('should return a graph with nodes and edges by actor', async () => { const response = await postGraph(supertest, { query: { - actorIds: ['admin@example.com'], eventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin@example.com', + }, + }, + ], + }, + }, }, }).expect(result(200)); expect(response.body).to.have.property('nodes').length(3); expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); response.body.nodes.forEach((node: any) => { expect(node).to.have.property('color'); @@ -131,7 +177,6 @@ export default function (providerContext: FtrProviderContext) { it('should return a graph with nodes and edges by alert', async () => { const response = await postGraph(supertest, { query: { - actorIds: [], eventIds: ['kabcd1234efgh5678'], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', @@ -140,6 +185,7 @@ export default function (providerContext: FtrProviderContext) { expect(response.body).to.have.property('nodes').length(3); expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); response.body.nodes.forEach((node: any) => { expect(node).to.have.property('color'); @@ -161,7 +207,6 @@ export default function (providerContext: FtrProviderContext) { it('color of alert of failed event should be danger', async () => { const response = await postGraph(supertest, { query: { - actorIds: [], eventIds: ['failed-event'], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', @@ -170,6 +215,7 @@ export default function (providerContext: FtrProviderContext) { expect(response.body).to.have.property('nodes').length(3); expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); response.body.nodes.forEach((node: any) => { expect(node).to.have.property('color'); @@ -191,15 +237,26 @@ export default function (providerContext: FtrProviderContext) { it('color of event of failed event should be warning', async () => { const response = await postGraph(supertest, { query: { - actorIds: ['admin2@example.com'], eventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin2@example.com', + }, + }, + ], + }, + }, }, }).expect(result(200)); expect(response.body).to.have.property('nodes').length(3); expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); response.body.nodes.forEach((node: any) => { expect(node).to.have.property('color'); @@ -219,18 +276,29 @@ export default function (providerContext: FtrProviderContext) { }); }); - it('2 grouped of events, 1 failed, 1 success', async () => { + it('2 grouped events, 1 failed, 1 success', async () => { const response = await postGraph(supertest, { query: { - actorIds: ['admin3@example.com'], eventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin3@example.com', + }, + }, + ], + }, + }, }, }).expect(result(200)); expect(response.body).to.have.property('nodes').length(5); expect(response.body).to.have.property('edges').length(6); + expect(response.body).not.to.have.property('messages'); expect(response.body.nodes[0].shape).equal('group', 'Groups should be the first nodes'); @@ -247,11 +315,167 @@ export default function (providerContext: FtrProviderContext) { response.body.edges.forEach((edge: any) => { expect(edge).to.have.property('color'); expect(edge.color).equal( - edge.id.includes('outcome(failed)') ? 'warning' : 'primary', + edge.id.includes('outcome(failed)') || + (edge.id.includes('grp(') && !edge.id.includes('outcome(success)')) + ? 'warning' + : 'primary', + `edge color mismatched [edge: ${edge.id}] [actual: ${edge.color}]` + ); + }); + }); + + it('should support more than 1 eventIds', async () => { + const response = await postGraph(supertest, { + query: { + eventIds: ['kabcd1234efgh5678', 'failed-event'], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(5); + expect(response.body).to.have.property('edges').length(4); + expect(response.body).not.to.have.property('messages'); + + response.body.nodes.forEach((node: any) => { + expect(node).to.have.property('color'); + expect(node.color).equal( + 'danger', + `node color mismatched [node: ${node.id}] [actual: ${node.color}]` + ); + }); + + response.body.edges.forEach((edge: any) => { + expect(edge).to.have.property('color'); + expect(edge.color).equal( + 'danger', + `edge color mismatched [edge: ${edge.id}] [actual: ${edge.color}]` + ); + }); + }); + + it('should return a graph with nodes and edges by alert and actor', async () => { + const response = await postGraph(supertest, { + query: { + eventIds: ['kabcd1234efgh5678'], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin2@example.com', + }, + }, + ], + }, + }, + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(5); + expect(response.body).to.have.property('edges').length(4); + expect(response.body).not.to.have.property('messages'); + + response.body.nodes.forEach((node: any, idx: number) => { + expect(node).to.have.property('color'); + expect(node.color).equal( + idx <= 2 // First 3 nodes are expected to be colored as danger (ORDER MATTERS, alerts are expected to be first) + ? 'danger' + : node.shape === 'label' && node.id.includes('outcome(failed)') + ? 'warning' + : 'primary', + `node color mismatched [node: ${node.id}] [actual: ${node.color}]` + ); + }); + + response.body.edges.forEach((edge: any, idx: number) => { + expect(edge).to.have.property('color'); + expect(edge.color).equal( + idx <= 1 ? 'danger' : 'warning', `edge color mismatched [edge: ${edge.id}] [actual: ${edge.color}]` ); }); }); + + it('Should filter unknown targets', async () => { + const response = await postGraph(supertest, { + query: { + eventIds: [], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin5@example.com', + }, + }, + ], + }, + }, + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(0); + expect(response.body).to.have.property('edges').length(0); + expect(response.body).not.to.have.property('messages'); + }); + + it('Should return unknown targets', async () => { + const response = await postGraph(supertest, { + showUnknownTarget: true, + query: { + eventIds: [], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + match_phrase: { + 'actor.entity.id': 'admin5@example.com', + }, + }, + ], + }, + }, + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(3); + expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); + }); + + it('Should limit number of nodes', async () => { + const response = await postGraph(supertest, { + nodesLimit: 1, + query: { + eventIds: [], + start: '2024-09-01T00:00:00Z', + end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [ + { + exists: { + field: 'actor.entity.id', + }, + }, + ], + }, + }, + }, + }).expect(result(200)); + + expect(response.body).to.have.property('nodes').length(3); // Minimal number of nodes in a single relationship + expect(response.body).to.have.property('edges').length(2); + expect(response.body).to.have.property('messages').length(1); + expect(response.body.messages[0]).equal(ApiMessageCode.ReachedNodesLimit); + }); }); }); } diff --git a/x-pack/test/cloud_security_posture_api/utils.ts b/x-pack/test/cloud_security_posture_api/utils.ts index e64c583af386..210a081b9147 100644 --- a/x-pack/test/cloud_security_posture_api/utils.ts +++ b/x-pack/test/cloud_security_posture_api/utils.ts @@ -36,22 +36,23 @@ export const waitForPluginInitialized = ({ logger.debug('CSP plugin is initialized'); }); -export function result(status: number): CallbackHandler { +export function result(status: number, logger?: ToolingLog): CallbackHandler { return (err: any, res: Response) => { if ((res?.status || err.status) !== status) { - const e = new Error( + throw new Error( `Expected ${status} ,got ${res?.status || err.status} resp: ${ res?.body ? JSON.stringify(res.body) : err.text }` ); - throw e; + } else if (err) { + logger?.warning(`Error result ${err.text}`); } }; } export class EsIndexDataProvider { private es: EsClient; - private index: string; + private readonly index: string; constructor(es: EsClient, index: string) { this.es = es; diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts index 741d25291e8f..aaccdd0e9a41 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/cloud_security_posture/graph.ts @@ -12,6 +12,7 @@ import { } from '@kbn/core-http-common'; import { result } from '@kbn/test-suites-xpack/cloud_security_posture_api/utils'; import type { Agent } from 'supertest'; +import type { GraphRequest } from '@kbn/cloud-security-posture-common/types/graph/v1'; import type { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -19,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { const roleScopedSupertest = getService('roleScopedSupertest'); let supertestViewer: Pick; - const postGraph = (agent: Pick, body: any) => { + const postGraph = (agent: Pick, body: GraphRequest) => { const req = agent .post('/internal/cloud_security_posture/graph') .set(ELASTIC_HTTP_VERSION_HEADER, '1') @@ -48,7 +49,6 @@ export default function ({ getService }: FtrProviderContext) { it('should return an empty graph', async () => { const response = await postGraph(supertestViewer, { query: { - actorIds: [], eventIds: [], start: 'now-1d/d', end: 'now/d', @@ -57,20 +57,26 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body).to.have.property('nodes').length(0); expect(response.body).to.have.property('edges').length(0); + expect(response.body).not.to.have.property('messages'); }); it('should return a graph with nodes and edges by actor', async () => { const response = await postGraph(supertestViewer, { query: { - actorIds: ['admin@example.com'], eventIds: [], start: '2024-09-01T00:00:00Z', end: '2024-09-02T00:00:00Z', + esQuery: { + bool: { + filter: [{ match_phrase: { 'actor.entity.id': 'admin@example.com' } }], + }, + }, }, }).expect(result(200)); expect(response.body).to.have.property('nodes').length(3); expect(response.body).to.have.property('edges').length(2); + expect(response.body).not.to.have.property('messages'); response.body.nodes.forEach((node: any) => { expect(node).to.have.property('color'); From 0e1021afbfbe096d60f47ce8ce5fb25eb9306bd9 Mon Sep 17 00:00:00 2001 From: Andrea Del Rio Date: Mon, 11 Nov 2024 12:07:06 -0800 Subject: [PATCH 023/100] [Dashboards] Flyouts design cleanup (#199408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Updates the styles of the following flyouts to achieve consistency: - Create control: This one has been simplified from two-column layout to single column layout after the design process for ES|QL controls. I've also changed the setting "expand width to fill" to `off` by default after feedback from Graham and Nino (I also think it's the better default). - Control settings - Add panel - Create links panel - Add link - Add image - Add from library (to-do: `Search` input, `Types` input and `Tags` input should be switched to `compressed`) This PR tries to standardize buttons in flyout footers according to these [guidelines](https://www.figma.com/design/y6thIbIHWSXl3v2GzOTtrE/Dashboard-new-panel-guidelines?node-id=51-364&t=rdiJ19w3v5GKYjrx-1) which considers what we do in other applications such as Discover. For example, this PR replaces the label `Save and close` with `Save`. The behavior in Discover (see “Add field” flyout) is that clicking on `Save` will `Save AND close` the flyout. I think it’s safe to assume users will expect that behavior upon saving a flyout. Frame 2 Frame 3 Frame 4 Frame 5 Frame 6 Frame 7 Frame 8 ### Other changes Additionally I've modified the behavior of the actions (`delete`, `edit`) for `links`. Instead of always occupying a column, now they're absolutely positioned and will appear on top of each link on hover. This allows us to make better use of the space. ![CleanShot 2024-11-07 at 18 30 44](https://github.com/user-attachments/assets/85c07e17-06fd-4c38-a37b-8ec63d764972) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) - [ ] This will appear in the **Release Notes** and follow the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- src/plugins/controls/common/constants.ts | 2 +- .../components/control_group_editor.tsx | 8 +- .../control_group/control_group_strings.tsx | 2 +- .../init_controls_manager.test.ts | 4 +- .../open_edit_control_group_flyout.tsx | 3 + .../data_controls/data_control_constants.tsx | 18 +- .../data_controls/data_control_editor.tsx | 278 ++++++++---------- .../open_data_control_editor.tsx | 3 + .../options_list_editor_options.tsx | 3 + .../get_range_slider_control_factory.tsx | 1 + .../dashboard_panel_selection_flyout.tsx | 5 +- .../dashboard_app/top_nav/editor_menu.tsx | 2 +- .../add_panel_flyout/add_panel_flyout.tsx | 2 +- .../open_add_panel_flyout.tsx | 3 + .../image_editor/image_editor_flyout.test.tsx | 6 +- .../image_editor/image_editor_flyout.tsx | 26 +- .../image_editor/open_image_editor.tsx | 3 + .../dashboard_link_destination_picker.tsx | 1 + .../public/components/editor/link_editor.tsx | 7 +- .../components/editor/links_editor.scss | 5 +- .../public/components/editor/links_editor.tsx | 4 +- .../editor/links_editor_single_link.tsx | 52 ++-- .../external_link_destination_picker.tsx | 1 + .../links/public/components/links_strings.ts | 2 +- .../public/editor/open_editor_flyout.tsx | 3 +- .../dashboard_drilldown_options.tsx | 57 ++-- .../data_view_picker/data_view_picker.tsx | 1 + .../components/field_picker/field_picker.tsx | 1 + .../field_picker/field_type_filter.tsx | 2 +- .../url_drilldown_options.tsx | 53 ++-- .../translations/translations/fr-FR.json | 4 - .../translations/translations/ja-JP.json | 4 - .../translations/translations/zh-CN.json | 4 - 33 files changed, 272 insertions(+), 298 deletions(-) diff --git a/src/plugins/controls/common/constants.ts b/src/plugins/controls/common/constants.ts index d1434d4df2ae..afd6fe66f0df 100644 --- a/src/plugins/controls/common/constants.ts +++ b/src/plugins/controls/common/constants.ts @@ -16,7 +16,7 @@ export const CONTROL_CHAINING_OPTIONS = { NONE: 'NONE', HIERARCHICAL: 'HIERARCHI export const DEFAULT_CONTROL_WIDTH: ControlWidth = CONTROL_WIDTH_OPTIONS.MEDIUM; export const DEFAULT_CONTROL_LABEL_POSITION: ControlLabelPosition = CONTROL_LABEL_POSITION_OPTIONS.ONE_LINE; -export const DEFAULT_CONTROL_GROW: boolean = true; +export const DEFAULT_CONTROL_GROW: boolean = false; export const DEFAULT_CONTROL_CHAINING: ControlGroupChainingSystem = CONTROL_CHAINING_OPTIONS.HIERARCHICAL; export const DEFAULT_IGNORE_PARENT_SETTINGS = { diff --git a/src/plugins/controls/public/control_group/components/control_group_editor.tsx b/src/plugins/controls/public/control_group/components/control_group_editor.tsx index 8f1ccb4d699b..cb21c23bc9ce 100644 --- a/src/plugins/controls/public/control_group/components/control_group_editor.tsx +++ b/src/plugins/controls/public/control_group/components/control_group_editor.tsx @@ -72,7 +72,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager return ( <> - +

{ControlGroupStrings.management.getFlyoutTitle()}

@@ -80,7 +80,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager { onCancel(); }} @@ -204,7 +204,7 @@ export const ControlGroupEditor = ({ onCancel, onSave, onDeleteAll, stateManager { diff --git a/src/plugins/controls/public/control_group/control_group_strings.tsx b/src/plugins/controls/public/control_group/control_group_strings.tsx index b8f6a11abf83..f5c92d987b27 100644 --- a/src/plugins/controls/public/control_group/control_group_strings.tsx +++ b/src/plugins/controls/public/control_group/control_group_strings.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; export const ControlGroupStrings = { getSaveChangesTitle: () => i18n.translate('controls.controlGroup.manageControl.saveChangesTitle', { - defaultMessage: 'Save and close', + defaultMessage: 'Save', }), getCancelTitle: () => i18n.translate('controls.controlGroup.manageControl.cancelTitle', { diff --git a/src/plugins/controls/public/control_group/init_controls_manager.test.ts b/src/plugins/controls/public/control_group/init_controls_manager.test.ts index 29998325664b..d88dc5452a0e 100644 --- a/src/plugins/controls/public/control_group/init_controls_manager.test.ts +++ b/src/plugins/controls/public/control_group/init_controls_manager.test.ts @@ -263,7 +263,7 @@ describe('getNewControlState', () => { test('should contain defaults when there are no existing controls', () => { const controlsManager = initControlsManager({}, new BehaviorSubject({})); expect(controlsManager.getNewControlState()).toEqual({ - grow: true, + grow: false, width: 'medium', dataViewId: undefined, }); @@ -284,7 +284,7 @@ describe('getNewControlState', () => { new BehaviorSubject(intialControlsState) ); expect(controlsManager.getNewControlState()).toEqual({ - grow: true, + grow: false, width: 'medium', dataViewId: 'myOtherDataViewId', }); diff --git a/src/plugins/controls/public/control_group/open_edit_control_group_flyout.tsx b/src/plugins/controls/public/control_group/open_edit_control_group_flyout.tsx index 54e35ab271b3..459913d98de0 100644 --- a/src/plugins/controls/public/control_group/open_edit_control_group_flyout.tsx +++ b/src/plugins/controls/public/control_group/open_edit_control_group_flyout.tsx @@ -101,6 +101,9 @@ export const openEditControlGroupFlyout = ( 'aria-label': i18n.translate('controls.controlGroup.manageControl', { defaultMessage: 'Edit control settings', }), + size: 'm', + maxWidth: 500, + paddingSize: 'm', outsideClickCloses: false, onClose: () => closeOverlay(overlay), } diff --git a/src/plugins/controls/public/controls/data_controls/data_control_constants.tsx b/src/plugins/controls/public/controls/data_controls/data_control_constants.tsx index 6b06bd8a5243..23d4c68f6c5d 100644 --- a/src/plugins/controls/public/controls/data_controls/data_control_constants.tsx +++ b/src/plugins/controls/public/controls/data_controls/data_control_constants.tsx @@ -21,14 +21,6 @@ export const DataControlEditorStrings = { defaultMessage: 'Edit control', }), dataSource: { - getFormGroupTitle: () => - i18n.translate('controls.controlGroup.manageControl.dataSource.formGroupTitle', { - defaultMessage: 'Data source', - }), - getFormGroupDescription: () => - i18n.translate('controls.controlGroup.manageControl.dataSource.formGroupDescription', { - defaultMessage: 'Select the data view and field that you want to create a control for.', - }), getSelectDataViewMessage: () => i18n.translate('controls.controlGroup.manageControl.dataSource.selectDataViewMessage', { defaultMessage: 'Please select a data view', @@ -95,14 +87,6 @@ export const DataControlEditorStrings = { }, }, displaySettings: { - getFormGroupTitle: () => - i18n.translate('controls.controlGroup.manageControl.displaySettings.formGroupTitle', { - defaultMessage: 'Display settings', - }), - getFormGroupDescription: () => - i18n.translate('controls.controlGroup.manageControl.displaySettings.formGroupDescription', { - defaultMessage: 'Change how the control appears on your dashboard.', - }), getTitleInputTitle: () => i18n.translate('controls.controlGroup.manageControl.displaySettings.titleInputTitle', { defaultMessage: 'Label', @@ -133,7 +117,7 @@ export const DataControlEditorStrings = { }, getSaveChangesTitle: () => i18n.translate('controls.controlGroup.manageControl.saveChangesTitle', { - defaultMessage: 'Save and close', + defaultMessage: 'Save', }), getCancelTitle: () => i18n.translate('controls.controlGroup.manageControl.cancelTitle', { diff --git a/src/plugins/controls/public/controls/data_controls/data_control_editor.tsx b/src/plugins/controls/public/controls/data_controls/data_control_editor.tsx index 23fd95978ff8..a84425f350dc 100644 --- a/src/plugins/controls/public/controls/data_controls/data_control_editor.tsx +++ b/src/plugins/controls/public/controls/data_controls/data_control_editor.tsx @@ -15,7 +15,6 @@ import { EuiButtonEmpty, EuiButtonGroup, EuiCallOut, - EuiDescribedFormGroup, EuiFieldText, EuiFlexGroup, EuiFlexItem, @@ -250,20 +249,8 @@ export const DataControlEditor = - {DataControlEditorStrings.manageControl.controlTypeSettings.getFormGroupTitle( - controlFactory.getDisplayName() - )} - - } - description={DataControlEditorStrings.manageControl.controlTypeSettings.getFormGroupDescription( - controlFactory.getDisplayName() - )} - data-test-subj="control-editor-custom-settings" - > +
+ - +
); }, [fieldRegistry, controlFactory, initialState, editorState, controlGroupApi]); return ( <> - +

{!controlId // if no ID, then we are creating a new control ? DataControlEditorStrings.manageControl.getFlyoutCreateTitle() @@ -288,156 +275,144 @@ export const DataControlEditor = - {DataControlEditorStrings.manageControl.dataSource.getFormGroupTitle()}

} - description={DataControlEditorStrings.manageControl.dataSource.getFormGroupDescription()} - > - {!editorConfig?.hideDataViewSelector && ( - - {dataViewListError ? ( - -

{dataViewListError.message}

-
- ) : ( - { - setEditorState({ ...editorState, dataViewId: newDataViewId }); - setSelectedControlType(undefined); - }} - trigger={{ - label: - selectedDataView?.getName() ?? - DataControlEditorStrings.manageControl.dataSource.getSelectDataViewMessage(), - }} - selectableProps={{ isLoading: dataViewListLoading }} - /> - )} -
- )} - - - {fieldListError ? ( + {!editorConfig?.hideDataViewSelector && ( + + {dataViewListError ? ( -

{fieldListError.message}

+

{dataViewListError.message}

) : ( - { - const customPredicate = editorConfig?.fieldFilterPredicate?.(field) ?? true; - return Boolean(fieldRegistry?.[field.name]) && customPredicate; + { + setEditorState({ ...editorState, dataViewId: newDataViewId }); + setSelectedControlType(undefined); }} - selectedFieldName={editorState.fieldName} - dataView={selectedDataView} - onSelectField={(field) => { - setEditorState({ ...editorState, fieldName: field.name }); - - /** - * make sure that the new field is compatible with the selected control type and, if it's not, - * reset the selected control type to the **first** compatible control type - */ - const newCompatibleControlTypes = - fieldRegistry?.[field.name]?.compatibleControlTypes ?? []; - if ( - !selectedControlType || - !newCompatibleControlTypes.includes(selectedControlType!) - ) { - setSelectedControlType(newCompatibleControlTypes[0]); - } - - /** - * set the control title (i.e. the one set by the user) + default title (i.e. the field display name) - */ - const newDefaultTitle = field.displayName ?? field.name; - setDefaultPanelTitle(newDefaultTitle); - const currentTitle = editorState.title; - if (!currentTitle || currentTitle === newDefaultTitle) { - setPanelTitle(newDefaultTitle); - } - - setControlOptionsValid(true); // reset options state + trigger={{ + label: + selectedDataView?.getName() ?? + DataControlEditorStrings.manageControl.dataSource.getSelectDataViewMessage(), }} - selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }} + selectableProps={{ isLoading: dataViewListLoading }} /> )}
+ )} + + + {fieldListError ? ( + +

{fieldListError.message}

+
+ ) : ( + { + const customPredicate = editorConfig?.fieldFilterPredicate?.(field) ?? true; + return Boolean(fieldRegistry?.[field.name]) && customPredicate; + }} + selectedFieldName={editorState.fieldName} + dataView={selectedDataView} + onSelectField={(field) => { + setEditorState({ ...editorState, fieldName: field.name }); + + /** + * make sure that the new field is compatible with the selected control type and, if it's not, + * reset the selected control type to the **first** compatible control type + */ + const newCompatibleControlTypes = + fieldRegistry?.[field.name]?.compatibleControlTypes ?? []; + if ( + !selectedControlType || + !newCompatibleControlTypes.includes(selectedControlType!) + ) { + setSelectedControlType(newCompatibleControlTypes[0]); + } + + /** + * set the control title (i.e. the one set by the user) + default title (i.e. the field display name) + */ + const newDefaultTitle = field.displayName ?? field.name; + setDefaultPanelTitle(newDefaultTitle); + const currentTitle = editorState.title; + if (!currentTitle || currentTitle === newDefaultTitle) { + setPanelTitle(newDefaultTitle); + } + + setControlOptionsValid(true); // reset options state + }} + selectableProps={{ isLoading: dataViewListLoading || dataViewLoading }} + /> + )} +
+ + {/* wrapping in `div` so that focus gets passed properly to the form row */} +
+ +
+
+ + { + setPanelTitle(e.target.value ?? ''); + setEditorState({ + ...editorState, + title: e.target.value === '' ? undefined : e.target.value, + }); + }} + /> + + {!editorConfig?.hideWidthSettings && ( - {/* wrapping in `div` so that focus gets passed properly to the form row */}
- + setEditorState({ ...editorState, width: newWidth as ControlWidth }) + } + /> + + setEditorState({ ...editorState, grow: !editorState.grow })} + data-test-subj="control-editor-grow-switch" />
- - {DataControlEditorStrings.manageControl.displaySettings.getFormGroupTitle()} - } - description={DataControlEditorStrings.manageControl.displaySettings.getFormGroupDescription()} - > - - { - setPanelTitle(e.target.value ?? ''); - setEditorState({ - ...editorState, - title: e.target.value === '' ? undefined : e.target.value, - }); - }} - /> - - {!editorConfig?.hideWidthSettings && ( - -
- - setEditorState({ ...editorState, width: newWidth as ControlWidth }) - } - /> - - setEditorState({ ...editorState, grow: !editorState.grow })} - data-test-subj="control-editor-grow-switch" - /> -
-
- )} -
+ )} {!editorConfig?.hideAdditionalSettings && CustomSettingsComponent} {controlId && ( <> @@ -464,7 +439,6 @@ export const DataControlEditor = { onCancel(editorState); }} @@ -476,7 +450,7 @@ export const DataControlEditor = closeOverlay(overlay), } ); diff --git a/src/plugins/controls/public/controls/data_controls/options_list_control/components/options_list_editor_options.tsx b/src/plugins/controls/public/controls/data_controls/options_list_control/components/options_list_editor_options.tsx index e9dad12be562..f07a7cc6c58b 100644 --- a/src/plugins/controls/public/controls/data_controls/options_list_control/components/options_list_editor_options.tsx +++ b/src/plugins/controls/public/controls/data_controls/options_list_control/components/options_list_editor_options.tsx @@ -131,6 +131,7 @@ export const OptionsListEditorOptions = ({ data-test-subj="optionsListControl__selectionOptionsRadioGroup" > { @@ -146,6 +147,7 @@ export const OptionsListEditorOptions = ({ data-test-subj="optionsListControl__searchOptionsRadioGroup" > { @@ -158,6 +160,7 @@ export const OptionsListEditorOptions = ({ )} { const newStep = event.target.valueAsNumber; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/dashboard_panel_selection_flyout.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/dashboard_panel_selection_flyout.tsx index dbb86046def0..e6adece8ab36 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/dashboard_panel_selection_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/add_new_panel/dashboard_panel_selection_flyout.tsx @@ -125,7 +125,7 @@ export const DashboardPanelSelectionListFlyout: React.FC< return ( <> - +

{ @@ -281,7 +282,7 @@ export const DashboardPanelSelectionListFlyout: React.FC< diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx index 2cad63c44202..cf7f9c65c661 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/editor_menu.tsx @@ -43,7 +43,7 @@ export const EditorMenu = ({ createNewVisType, isDisabled }: EditorMenuProps) => function openDashboardPanelSelectionFlyout() { const flyoutPanelPaddingSize: ComponentProps< typeof DashboardPanelSelectionListFlyout - >['paddingSize'] = 'l'; + >['paddingSize'] = 'm'; const mount = toMountPoint( React.createElement(function () { diff --git a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx index b83ebbcb49d6..b334dbcb5857 100644 --- a/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/add_panel_flyout/add_panel_flyout.tsx @@ -189,7 +189,7 @@ export const AddPanelFlyout = ({ return ( <> - +

{i18n.translate('embeddableApi.addPanel.Title', { defaultMessage: 'Add from library' })}

diff --git a/src/plugins/embeddable/public/add_panel_flyout/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/add_panel_flyout/open_add_panel_flyout.tsx index 160289d0d1c2..9ba3c00a7374 100644 --- a/src/plugins/embeddable/public/add_panel_flyout/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/add_panel_flyout/open_add_panel_flyout.tsx @@ -52,6 +52,9 @@ export const openAddPanelFlyout = ({ if (onClose) onClose(); overlayRef.close(); }, + size: 'm', + maxWidth: 500, + paddingSize: 'm', 'data-test-subj': 'dashboardAddPanel', 'aria-labelledby': modalTitleId, } diff --git a/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.test.tsx b/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.test.tsx index 265f162d04f6..f052f0526a94 100644 --- a/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.test.tsx +++ b/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.test.tsx @@ -43,11 +43,11 @@ const ImageEditor = (props: Partial) => { ); }; -test('should call onCancel when "Close" clicked', async () => { +test('should call onCancel when "Cancel" clicked', async () => { const onCancel = jest.fn(); const { getByText } = render(); - expect(getByText('Close')).toBeVisible(); - await userEvent.click(getByText('Close')); + expect(getByText('Cancel')).toBeVisible(); + await userEvent.click(getByText('Cancel')); expect(onCancel).toBeCalled(); }); diff --git a/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.tsx b/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.tsx index 2c57f25db6c8..1a5ee3bc64e1 100644 --- a/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.tsx +++ b/src/plugins/image_embeddable/public/components/image_editor/image_editor_flyout.tsx @@ -121,7 +121,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) { return ( <> - +

{isEditing ? ( - - + + + setSrcType('file')} isSelected={srcType === 'file'}> - - + {srcType === 'file' && ( <> {isDraftImageConfigValid ? ( @@ -238,7 +238,7 @@ export function ImageEditorFlyout(props: ImageEditorFlyoutProps) { />

} - titleSize={'s'} + titleSize={'xs'} /> ) : ( )} - - + )} - - - - - - + diff --git a/src/plugins/image_embeddable/public/components/image_editor/open_image_editor.tsx b/src/plugins/image_embeddable/public/components/image_editor/open_image_editor.tsx index ae8ced88d14e..f730147cb0d2 100644 --- a/src/plugins/image_embeddable/public/components/image_editor/open_image_editor.tsx +++ b/src/plugins/image_embeddable/public/components/image_editor/open_image_editor.tsx @@ -79,6 +79,9 @@ export const openImageEditor = async ({ onClose: () => { onCancel(); }, + size: 'm', + maxWidth: 500, + paddingSize: 'm', ownFocus: true, 'data-test-subj': 'createImageEmbeddableFlyout', } diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx index b062b9befa28..ab5b92332704 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_destination_picker.tsx @@ -119,6 +119,7 @@ export const DashboardLinkDestinationPicker = ({ return ( onClose()} > - +

{link ? LinksStrings.editor.getEditLinkTitle() @@ -113,6 +113,7 @@ export const LinkEditor = ({ { @@ -131,6 +132,7 @@ export const LinkEditor = ({ /> onClose()} - iconType="cross" data-test-subj="links--linkEditor--closeBtn" > {LinksStrings.editor.getCancelButtonLabel()} @@ -160,6 +162,7 @@ export const LinkEditor = ({ { // this check should always be true, since the button is disabled otherwise - this is just for type safety diff --git a/src/plugins/links/public/components/editor/links_editor.scss b/src/plugins/links/public/components/editor/links_editor.scss index 02961c7d5f5c..c33b95350df9 100644 --- a/src/plugins/links/public/components/editor/links_editor.scss +++ b/src/plugins/links/public/components/editor/links_editor.scss @@ -3,7 +3,7 @@ .linksPanelEditor { .linkEditor { @include euiFlyout; - max-inline-size: $euiSizeXXL * 18; // 40px * 18 = 720px + max-inline-size: $euiSizeXS * 125; // 4px * 125 = 500px &.in { animation: euiFlyoutOpenAnimation $euiAnimSpeedNormal $euiAnimSlightResistance; @@ -59,6 +59,9 @@ } .links_hoverActions { + background-color: $euiColorEmptyShade; + position: absolute; + right: $euiSizeL; opacity: 0; visibility: hidden; transition: visibility $euiAnimSpeedNormal, opacity $euiAnimSpeedNormal; diff --git a/src/plugins/links/public/components/editor/links_editor.tsx b/src/plugins/links/public/components/editor/links_editor.tsx index 93ca47e364c5..8fa33fd4ebca 100644 --- a/src/plugins/links/public/components/editor/links_editor.tsx +++ b/src/plugins/links/public/components/editor/links_editor.tsx @@ -167,7 +167,7 @@ const LinksEditor = ({ - +

{isEditingExisting ? LinksStrings.editor.panelEditor.getEditFlyoutTitle() @@ -251,7 +251,6 @@ const LinksEditor = ({ @@ -268,6 +267,7 @@ const LinksEditor = ({ data-test-subj="links--panelEditor--saveByReferenceTooltip" > - - - - - - - - - - - - - - + + + + + + + + + + + + ); diff --git a/src/plugins/links/public/components/external_link/external_link_destination_picker.tsx b/src/plugins/links/public/components/external_link/external_link_destination_picker.tsx index dfdc6e0589e6..5b8522b39960 100644 --- a/src/plugins/links/public/components/external_link/external_link_destination_picker.tsx +++ b/src/plugins/links/public/components/external_link/external_link_destination_picker.tsx @@ -54,6 +54,7 @@ export const ExternalLinkDestinationPicker = ({ return (
i18n.translate('links.editor.cancelButtonLabel', { - defaultMessage: 'Close', + defaultMessage: 'Cancel', }), panelEditor: { getLinksTitle: () => diff --git a/src/plugins/links/public/editor/open_editor_flyout.tsx b/src/plugins/links/public/editor/open_editor_flyout.tsx index 041672e89dbb..87b1ab4e21ff 100644 --- a/src/plugins/links/public/editor/open_editor_flyout.tsx +++ b/src/plugins/links/public/editor/open_editor_flyout.tsx @@ -137,7 +137,8 @@ export async function openEditorFlyout({ ), { id: flyoutId, - maxWidth: 720, + maxWidth: 500, + paddingSize: 'm', ownFocus: true, onClose: onCancel, outsideClickCloses: false, diff --git a/src/plugins/presentation_util/public/components/dashboard_drilldown_options/dashboard_drilldown_options.tsx b/src/plugins/presentation_util/public/components/dashboard_drilldown_options/dashboard_drilldown_options.tsx index 921560f7a222..63ff89da2ec1 100644 --- a/src/plugins/presentation_util/public/components/dashboard_drilldown_options/dashboard_drilldown_options.tsx +++ b/src/plugins/presentation_util/public/components/dashboard_drilldown_options/dashboard_drilldown_options.tsx @@ -8,7 +8,7 @@ */ import React from 'react'; -import { EuiFormRow, EuiSwitch } from '@elastic/eui'; +import { EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui'; import { DashboardDrilldownOptions } from './types'; import { dashboardDrilldownConfigStrings } from '../../i18n/dashboard_drilldown_config'; @@ -24,32 +24,35 @@ export const DashboardDrilldownOptionsComponent = ({ }: DashboardDrilldownOptionsProps) => { return ( <> - - onOptionChange({ useCurrentFilters: !options.useCurrentFilters })} - data-test-subj="dashboardDrillDownOptions--useCurrentFilters--checkbox" - /> - - - onOptionChange({ useCurrentDateRange: !options.useCurrentDateRange })} - data-test-subj="dashboardDrillDownOptions--useCurrentDateRange--checkbox" - /> - - - onOptionChange({ openInNewTab: !options.openInNewTab })} - data-test-subj="dashboardDrillDownOptions--openInNewTab--checkbox" - /> + +
+ onOptionChange({ useCurrentFilters: !options.useCurrentFilters })} + data-test-subj="dashboardDrillDownOptions--useCurrentFilters--checkbox" + /> + + onOptionChange({ useCurrentDateRange: !options.useCurrentDateRange })} + data-test-subj="dashboardDrillDownOptions--useCurrentDateRange--checkbox" + /> + + onOptionChange({ openInNewTab: !options.openInNewTab })} + data-test-subj="dashboardDrillDownOptions--openInNewTab--checkbox" + /> +
); diff --git a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx index e985e9bec357..1c8466097bd9 100644 --- a/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx +++ b/src/plugins/presentation_util/public/components/data_view_picker/data_view_picker.tsx @@ -52,6 +52,7 @@ export function DataViewPicker({ data-test-subj="open-data-view-picker" onClick={() => setPopoverIsOpen(!isPopoverOpen)} label={label} + size="s" fullWidth {...colorProp} {...rest} diff --git a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx index daac202f21b6..0b81cfd66156 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_picker.tsx @@ -140,6 +140,7 @@ export const FieldPicker = ({ placeholder: i18n.translate('presentationUtil.fieldSearch.searchPlaceHolder', { defaultMessage: 'Search field names', }), + compressed: true, disabled: Boolean(selectableProps?.isLoading), inputRef: setSearchRef, }} diff --git a/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx index d2e929b8a9a8..4212668599a0 100644 --- a/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx +++ b/src/plugins/presentation_util/public/components/field_picker/field_type_filter.tsx @@ -63,7 +63,7 @@ export function FieldTypeFilter({ ); return ( - + { return ( <> - - onOptionChange({ openInNewTab: !options.openInNewTab })} - data-test-subj="urlDrilldownOpenInNewTab" - /> - - - - {txtUrlTemplateEncodeUrl} - - {txtUrlTemplateEncodeDescription} - - } - checked={options.encodeUrl} - onChange={() => onOptionChange({ encodeUrl: !options.encodeUrl })} - data-test-subj="urlDrilldownEncodeUrl" - /> + +
+ onOptionChange({ openInNewTab: !options.openInNewTab })} + data-test-subj="urlDrilldownOpenInNewTab" + /> + + + {txtUrlTemplateEncodeUrl} + + {txtUrlTemplateEncodeDescription} + + } + checked={options.encodeUrl} + onChange={() => onOptionChange({ encodeUrl: !options.encodeUrl })} + data-test-subj="urlDrilldownEncodeUrl" + /> +
); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 9d8958957421..f03c50900151 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -862,11 +862,7 @@ "controls.controlGroup.manageControl.dataSource.dataViewTitle": "Vue de données", "controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "Erreur lors du chargement de la liste des champs", "controls.controlGroup.manageControl.dataSource.fieldTitle": "Champ", - "controls.controlGroup.manageControl.dataSource.formGroupDescription": "Sélectionnez la vue de données et le champ pour lesquels vous voulez créer un contrôle.", - "controls.controlGroup.manageControl.dataSource.formGroupTitle": "Source de données", "controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "Veuillez sélectionner une vue de données", - "controls.controlGroup.manageControl.displaySettings.formGroupDescription": "Changez la manière dont le contrôle apparaît sur votre tableau de bord.", - "controls.controlGroup.manageControl.displaySettings.formGroupTitle": "Paramètres d'affichage", "controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "Augmenter la largeur en fonction de l'espace disponible", "controls.controlGroup.manageControl.displaySettings.titleInputTitle": "Étiquette", "controls.controlGroup.manageControl.displaySettings.widthInputTitle": "Largeur minimale", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c923e7c19a85..0b5587b2bd91 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -864,11 +864,7 @@ "controls.controlGroup.manageControl.dataSource.dataViewTitle": "データビュー", "controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "フィールドリストの読み込みエラー", "controls.controlGroup.manageControl.dataSource.fieldTitle": "フィールド", - "controls.controlGroup.manageControl.dataSource.formGroupDescription": "コントロールを作成するデータビューとフィールドを選択します。", - "controls.controlGroup.manageControl.dataSource.formGroupTitle": "データソース", "controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "データビューを選択してください", - "controls.controlGroup.manageControl.displaySettings.formGroupDescription": "ダッシュボードにコントロールを表示する方法を変更します。", - "controls.controlGroup.manageControl.displaySettings.formGroupTitle": "表示設定", "controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "空きスペースに合わせて幅を拡大", "controls.controlGroup.manageControl.displaySettings.titleInputTitle": "ラベル", "controls.controlGroup.manageControl.displaySettings.widthInputTitle": "最小幅", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ad65daedc740..4d4db174396e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -856,11 +856,7 @@ "controls.controlGroup.manageControl.dataSource.dataViewTitle": "数据视图", "controls.controlGroup.manageControl.dataSource.fieldListErrorTitle": "加载字段列表时出错", "controls.controlGroup.manageControl.dataSource.fieldTitle": "字段", - "controls.controlGroup.manageControl.dataSource.formGroupDescription": "选择要为其创建控件的数据视图和字段。", - "controls.controlGroup.manageControl.dataSource.formGroupTitle": "数据源", "controls.controlGroup.manageControl.dataSource.selectDataViewMessage": "请选择数据视图", - "controls.controlGroup.manageControl.displaySettings.formGroupDescription": "更改控件在仪表板上的显示方式。", - "controls.controlGroup.manageControl.displaySettings.formGroupTitle": "显示设置", "controls.controlGroup.manageControl.displaySettings.growSwitchTitle": "扩大宽度以适应可用空间", "controls.controlGroup.manageControl.displaySettings.titleInputTitle": "标签", "controls.controlGroup.manageControl.displaySettings.widthInputTitle": "最小宽度", From 4f53a1134724c7ee5c9387b05d748076c103ce75 Mon Sep 17 00:00:00 2001 From: "elastic-renovate-prod[bot]" <174716857+elastic-renovate-prod[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 21:08:40 +0100 Subject: [PATCH 024/100] Update dependency @elastic/elasticsearch to ^8.15.1 (main) (#196478) --- package.json | 2 +- .../catch_retryable_es_client_errors.test.ts | 39 ++++++++++--------- .../src/lib/shared/base_client.ts | 1 - .../src/tasks/install_elser.ts | 1 - .../src/random_sampler_wrapper.ts | 1 - ...sform_elastic_named_search_to_list_item.ts | 2 +- .../model_management/expanded_row.tsx | 1 + .../models/model_management/memory_usage.ts | 1 + .../enrich_signal_threat_matches.ts | 6 ++- .../get_signals_map_from_threat_index.ts | 4 +- .../indicator_match/threat_mapping/utils.ts | 4 +- .../factory/cti/event_enrichment/helpers.ts | 21 +++++----- yarn.lock | 18 ++++----- 13 files changed, 55 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index afda7cd4c912..4679049714d1 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "@elastic/datemath": "5.0.3", "@elastic/ebt": "^1.1.1", "@elastic/ecs": "^8.11.1", - "@elastic/elasticsearch": "^8.15.0", + "@elastic/elasticsearch": "^8.15.1", "@elastic/ems-client": "8.5.3", "@elastic/eui": "97.3.0", "@elastic/filesaver": "1.1.2", diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/catch_retryable_es_client_errors.test.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/catch_retryable_es_client_errors.test.ts index 1aeabb7e86de..c84b30cf1577 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/catch_retryable_es_client_errors.test.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/actions/catch_retryable_es_client_errors.test.ts @@ -72,24 +72,25 @@ describe('catchRetryableEsClientErrors', () => { type: 'retryable_es_client_error', }); }); - it('ResponseError with retryable status code', async () => { - const statusCodes = [503, 401, 403, 408, 410, 429]; - return Promise.all( - statusCodes.map(async (status) => { - const error = new esErrors.ResponseError( - elasticsearchClientMock.createApiResponse({ - statusCode: status, - body: { error: { type: 'reason' } }, - }) - ); - expect( - ((await Promise.reject(error).catch(catchRetryableEsClientErrors)) as any).left - ).toMatchObject({ - message: 'reason', - type: 'retryable_es_client_error', - }); - }) - ); - }); + it.each([503, 401, 403, 408, 410, 429])( + 'ResponseError with retryable status code (%d)', + async (status) => { + const error = new esErrors.ResponseError( + elasticsearchClientMock.createApiResponse({ + statusCode: status, + body: { error: { type: 'reason' } }, + }) + ); + expect( + ((await Promise.reject(error).catch(catchRetryableEsClientErrors)) as any).left + ).toMatchObject({ + message: + status === 410 + ? 'This API is unavailable in the version of Elasticsearch you are using.' + : 'reason', + type: 'retryable_es_client_error', + }); + } + ); }); }); diff --git a/packages/kbn-apm-synthtrace/src/lib/shared/base_client.ts b/packages/kbn-apm-synthtrace/src/lib/shared/base_client.ts index ed6d1b813184..7fd639331af8 100644 --- a/packages/kbn-apm-synthtrace/src/lib/shared/base_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/shared/base_client.ts @@ -55,7 +55,6 @@ export class SynthtraceEsClient { await this.client.indices.resolveIndex({ name: this.indices.join(','), expand_wildcards: ['open', 'hidden'], - // @ts-expect-error ignore_unavailable is not in the type definition, but it is accepted by es ignore_unavailable: true, }) ).indices.map((index: { name: string }) => index.name) diff --git a/x-pack/packages/ai-infra/product-doc-artifact-builder/src/tasks/install_elser.ts b/x-pack/packages/ai-infra/product-doc-artifact-builder/src/tasks/install_elser.ts index 037a9e809d1e..09dc85b81619 100644 --- a/x-pack/packages/ai-infra/product-doc-artifact-builder/src/tasks/install_elser.ts +++ b/x-pack/packages/ai-infra/product-doc-artifact-builder/src/tasks/install_elser.ts @@ -60,7 +60,6 @@ const waitUntilDeployed = async ({ model_id: modelId, }); const deploymentStats = statsRes.trained_model_stats[0]?.deployment_stats; - // @ts-expect-error deploymentStats.nodes not defined as array even if it is. if (!deploymentStats || deploymentStats.nodes.length === 0) { await sleep(delay); continue; diff --git a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts index 5054833ac7dd..39d26509422a 100644 --- a/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts +++ b/x-pack/packages/ml/random_sampler_utils/src/random_sampler_wrapper.ts @@ -69,7 +69,6 @@ export const createRandomSamplerWrapper = (options: RandomSamplerOptions) => { return { [aggName]: { - // @ts-expect-error `random_sampler` is not yet part of `AggregationsAggregationContainer` random_sampler: { probability, ...(options.seed ? { seed: options.seed } : {}), diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts b/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts index 0a3632efe919..abdffd19eca7 100644 --- a/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts +++ b/x-pack/plugins/lists/server/services/utils/transform_elastic_named_search_to_list_item.ts @@ -34,7 +34,7 @@ export const transformElasticNamedSearchToListItem = ({ }: TransformElasticMSearchToListItemOptions): SearchListItemArraySchema => { return value.map((singleValue, index) => { const matchingHits = response.hits.hits.filter((hit) => { - if (hit.matched_queries != null) { + if (hit.matched_queries != null && Array.isArray(hit.matched_queries)) { return hit.matched_queries.some((matchedQuery) => { const [matchedQueryIndex] = matchedQuery.split('.'); return matchedQueryIndex === `${index}`; diff --git a/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx index e9db6b4e4f59..f7e95e3eda52 100644 --- a/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/model_management/expanded_row.tsx @@ -182,6 +182,7 @@ export const ExpandedRow: FC = ({ item }) => { key: `${perDeploymentStat.deployment_id}_${nodeName}`, ...perDeploymentStat, ...modelSizeStats, + // @ts-expect-error `throughput_last_minute` is not declared in ES Types node: { ...pick(n, [ 'average_inference_time_ms', diff --git a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts index 6e2931dbbe06..6e9121a133bb 100644 --- a/x-pack/plugins/ml/server/models/model_management/memory_usage.ts +++ b/x-pack/plugins/ml/server/models/model_management/memory_usage.ts @@ -181,6 +181,7 @@ export class MemoryUsageService { const mlNodes = Object.entries(response.nodes).filter(([, node]) => node.roles.includes('ml')); + // @ts-expect-error `throughput_last_minute` is not declared in ES Types const nodeDeploymentStatsResponses: NodeDeploymentStatsResponse[] = mlNodes.map( ([nodeId, node]) => { const nodeFields = pick(node, NODE_FIELDS) as RequiredNodeFields; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts index 8f98eab1a93e..0d9882fe8aec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/enrich_signal_threat_matches.ts @@ -24,8 +24,10 @@ export const groupAndMergeSignalMatches = (signalHits: SignalSourceHit[]): Signa if (existingSignalHit == null) { acc[signalId] = signalHit; } else { - const existingQueries = existingSignalHit?.matched_queries ?? []; - const newQueries = signalHit.matched_queries ?? []; + const existingQueries = Array.isArray(existingSignalHit?.matched_queries) + ? existingSignalHit.matched_queries + : []; + const newQueries = Array.isArray(signalHit.matched_queries) ? signalHit.matched_queries : []; existingSignalHit.matched_queries = [...existingQueries, ...newQueries]; acc[signalId] = existingSignalHit; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts index 309516a57335..9694d37aab0a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/get_signals_map_from_threat_index.ts @@ -90,7 +90,9 @@ export async function getSignalsQueryMapFromThreatIndex( while (maxThreatsReachedMap.size < eventsCount && threatList?.hits.hits.length > 0) { threatList.hits.hits.forEach((threatHit) => { - const matchedQueries = threatHit?.matched_queries || []; + const matchedQueries = Array.isArray(threatHit?.matched_queries) + ? threatHit.matched_queries + : []; matchedQueries.forEach((matchedQuery) => { const decodedQuery = decodeThreatMatchNamedQuery(matchedQuery); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts index da72d121c371..347ea5d1d94c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/utils.ts @@ -189,7 +189,9 @@ export const decodeThreatMatchNamedQuery = (encoded: string): DecodedThreatNamed export const extractNamedQueries = ( hit: SignalSourceHit | ThreatListItem ): DecodedThreatNamedQuery[] => - hit.matched_queries?.map((match) => decodeThreatMatchNamedQuery(match)) ?? []; + Array.isArray(hit.matched_queries) + ? hit.matched_queries.map((match) => decodeThreatMatchNamedQuery(match)) + : []; export const buildExecutionIntervalValidator: (interval: string) => () => void = (interval) => { const intervalDuration = parseInterval(interval); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/helpers.ts index e15aceb8a713..54af298d11a3 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/helpers.ts @@ -46,16 +46,19 @@ export const buildIndicatorShouldClauses = ( export const buildIndicatorEnrichments = (hits: estypes.SearchHit[]): CtiEnrichment[] => { return hits.flatMap(({ matched_queries: matchedQueries, ...hit }) => { return ( - matchedQueries?.reduce((enrichments, matchedQuery) => { - if (isValidEventField(matchedQuery)) { - enrichments.push({ - ...hit.fields, - ...buildIndicatorMatchedFields(hit, matchedQuery), - }); - } + (Array.isArray(matchedQueries) ? matchedQueries : [])?.reduce( + (enrichments, matchedQuery) => { + if (isValidEventField(matchedQuery)) { + enrichments.push({ + ...hit.fields, + ...buildIndicatorMatchedFields(hit, matchedQuery), + }); + } - return enrichments; - }, []) ?? [] + return enrichments; + }, + [] + ) ?? [] ); }); }; diff --git a/yarn.lock b/yarn.lock index 5faf426cf4d2..b4023f405d87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1721,12 +1721,12 @@ "@elastic/transport" "^8.3.1" tslib "^2.4.0" -"@elastic/elasticsearch@^8.15.0": - version "8.15.0" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.15.0.tgz#cb29b3ae33203c545d435cf3dc4b557c8b4961d5" - integrity sha512-mG90EMdTDoT6GFSdqpUAhWK9LGuiJo6tOWqs0Usd/t15mPQDj7ZqHXfCBqNkASZpwPZpbAYVjd57S6nbUBINCg== +"@elastic/elasticsearch@^8.15.1": + version "8.15.1" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.15.1.tgz#ca294ba11ed1514bf87d4a2e253b11f6cefd8552" + integrity sha512-L3YzSaxrasMMGtcxnktiUDjS5f177L0zpHsBH+jL0LgPhdMk9xN/VKrAaYzvri86IlV5IbveA0ANV6o/BDUmhQ== dependencies: - "@elastic/transport" "^8.7.0" + "@elastic/transport" "^8.8.1" tslib "^2.4.0" "@elastic/ems-client@8.5.3": @@ -1906,10 +1906,10 @@ undici "^5.28.3" yaml "^2.2.2" -"@elastic/transport@^8.3.1", "@elastic/transport@^8.7.0": - version "8.7.0" - resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.7.0.tgz#006987fc5583f61c266e0b1003371e82efc7a6b5" - integrity sha512-IqXT7a8DZPJtqP2qmX1I2QKmxYyN27kvSW4g6pInESE1SuGwZDp2FxHJ6W2kwmYOJwQdAt+2aWwzXO5jHo9l4A== +"@elastic/transport@^8.3.1", "@elastic/transport@^8.8.1": + version "8.8.1" + resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.8.1.tgz#d64244907bccdad5626c860b492faeef12194b1f" + integrity sha512-4RQIiChwNIx3B0O+2JdmTq/Qobj6+1g2RQnSv1gt4V2SVfAYjGwOKu0ZMKEHQOXYNG6+j/Chero2G9k3/wXLEw== dependencies: "@opentelemetry/api" "1.x" debug "^4.3.4" From 482e3f42234bc5e456f965d10ad028f71b255f6a Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 11 Nov 2024 14:09:32 -0600 Subject: [PATCH 025/100] skip failing test suite (#199648,#199700) --- .../components/step_define_rule/index.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx index cc8f2abda9c4..9bcf35fdb13c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx @@ -202,7 +202,9 @@ const onOpenTimeline = jest.fn(); const COMBO_BOX_TOGGLE_BUTTON_TEST_ID = 'comboBoxToggleListButton'; const VERSION_INPUT_TEST_ID = 'relatedIntegrationVersionDependency'; -describe('StepDefineRule', () => { +// Failing: See https://github.com/elastic/kibana/issues/199648 +// Failing: See https://github.com/elastic/kibana/issues/199700 +describe.skip('StepDefineRule', () => { beforeEach(() => { jest.clearAllMocks(); mockUseRuleFromTimeline.mockReturnValue({ onOpenTimeline, loading: false }); From be949d66e43ae24e9bce4a13a4613ad00e1dce9a Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 11 Nov 2024 15:17:46 -0500 Subject: [PATCH 026/100] [Response Ops][Task Manager] Adding background task to mark removed task types as `unrecognized` (#199057) Resolves https://github.com/elastic/kibana/issues/192686 ## Summary Creates a background task to search for removed task types and mark them as unrecognized. Removes the current logic that does this during the task claim cycle for both task claim strategies. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/task_manager/server/plugin.ts | 13 +- .../server/polling_lifecycle.test.ts | 1 - .../task_manager/server/polling_lifecycle.ts | 3 - .../mark_available_tasks_as_claimed.test.ts | 5 - .../mark_available_tasks_as_claimed.ts | 5 - .../server/queries/task_claiming.test.ts | 2 - .../server/queries/task_claiming.ts | 4 - ...mark_removed_tasks_as_unrecognized.test.ts | 266 ++++++++++++++ .../mark_removed_tasks_as_unrecognized.ts | 150 ++++++++ .../server/task_claimers/index.ts | 1 - .../task_claimers/strategy_mget.test.ts | 341 +----------------- .../server/task_claimers/strategy_mget.ts | 67 +--- .../strategy_update_by_query.test.ts | 133 ------- .../task_claimers/strategy_update_by_query.ts | 6 +- x-pack/plugins/task_manager/tsconfig.json | 3 +- .../task_manager_fixture/server/plugin.ts | 28 ++ .../alerting/group4/scheduled_task_id.ts | 6 + .../sample_task_plugin/server/init_routes.ts | 28 ++ .../check_registered_task_types.ts | 1 + .../task_management_removed_types.ts | 6 + .../server/init_routes.ts | 28 ++ .../task_management_removed_types.ts | 6 + 22 files changed, 547 insertions(+), 556 deletions(-) create mode 100644 x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.test.ts create mode 100644 x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.ts diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 45960195be21..cd820d1e7078 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -29,7 +29,7 @@ import { TaskManagerConfig } from './config'; import { createInitialMiddleware, addMiddlewareToChain, Middleware } from './lib/middleware'; import { removeIfExists } from './lib/remove_if_exists'; import { setupSavedObjects, BACKGROUND_TASK_NODE_SO_NAME, TASK_SO_NAME } from './saved_objects'; -import { TaskDefinitionRegistry, TaskTypeDictionary, REMOVED_TYPES } from './task_type_dictionary'; +import { TaskDefinitionRegistry, TaskTypeDictionary } from './task_type_dictionary'; import { AggregationOpts, FetchResult, SearchOpts, TaskStore } from './task_store'; import { createManagedConfiguration } from './lib/create_managed_configuration'; import { TaskScheduling } from './task_scheduling'; @@ -45,6 +45,10 @@ import { metricsStream, Metrics } from './metrics'; import { TaskManagerMetricsCollector } from './metrics/task_metrics_collector'; import { TaskPartitioner } from './lib/task_partitioner'; import { getDefaultCapacity } from './lib/get_default_capacity'; +import { + registerMarkRemovedTasksAsUnrecognizedDefinition, + scheduleMarkRemovedTasksAsUnrecognizedDefinition, +} from './removed_tasks/mark_removed_tasks_as_unrecognized'; export interface TaskManagerSetupContract { /** @@ -221,6 +225,11 @@ export class TaskManagerPlugin } registerDeleteInactiveNodesTaskDefinition(this.logger, core.getStartServices, this.definitions); + registerMarkRemovedTasksAsUnrecognizedDefinition( + this.logger, + core.getStartServices, + this.definitions + ); if (this.config.unsafe.exclude_task_types.length) { this.logger.warn( @@ -332,7 +341,6 @@ export class TaskManagerPlugin this.taskPollingLifecycle = new TaskPollingLifecycle({ config: this.config!, definitions: this.definitions, - unusedTypes: REMOVED_TYPES, logger: this.logger, executionContext, taskStore, @@ -384,6 +392,7 @@ export class TaskManagerPlugin }); scheduleDeleteInactiveNodesTaskDefinition(this.logger, taskScheduling).catch(() => {}); + scheduleMarkRemovedTasksAsUnrecognizedDefinition(this.logger, taskScheduling).catch(() => {}); return { fetch: (opts: SearchOpts): Promise => taskStore.fetch(opts), diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts index 1f244f7f4c8a..a408bd3f634d 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -106,7 +106,6 @@ describe('TaskPollingLifecycle', () => { }, taskStore: mockTaskStore, logger: taskManagerLogger, - unusedTypes: [], definitions: new TaskTypeDictionary(taskManagerLogger), middleware: createInitialMiddleware(), startingCapacity: 20, diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index 0b1710ae7fa2..fb6776fa34f2 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -55,7 +55,6 @@ export interface ITaskEventEmitter { export type TaskPollingLifecycleOpts = { logger: Logger; definitions: TaskTypeDictionary; - unusedTypes: string[]; taskStore: TaskStore; config: TaskManagerConfig; middleware: Middleware; @@ -115,7 +114,6 @@ export class TaskPollingLifecycle implements ITaskEventEmitter this.pool.availableCapacity(taskType), taskPartitioner, diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts index 76df8b7ae558..fa1d1f749985 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts @@ -70,7 +70,6 @@ describe('mark_available_tasks_as_claimed', () => { fieldUpdates, claimableTaskTypes: definitions.getAllTypes(), skippedTaskTypes: [], - unusedTaskTypes: [], taskMaxAttempts: Array.from(definitions).reduce((accumulator, [type, { maxAttempts }]) => { return { ...accumulator, [type]: maxAttempts || defaultMaxAttempts }; }, {}), @@ -153,8 +152,6 @@ if (doc['task.runAt'].size()!=0) { ctx._source.task.status = "claiming"; ${Object.keys(fieldUpdates) .map((field) => `ctx._source.task.${field}=params.fieldUpdates.${field};`) .join(' ')} - } else if (params.unusedTaskTypes.contains(ctx._source.task.taskType)) { - ctx._source.task.status = "unrecognized"; } else { ctx.op = "noop"; }`, @@ -167,7 +164,6 @@ if (doc['task.runAt'].size()!=0) { }, claimableTaskTypes: ['sampleTask', 'otherTask'], skippedTaskTypes: [], - unusedTaskTypes: [], taskMaxAttempts: { sampleTask: 5, otherTask: 1, @@ -242,7 +238,6 @@ if (doc['task.runAt'].size()!=0) { fieldUpdates, claimableTaskTypes: ['foo', 'bar'], skippedTaskTypes: [], - unusedTaskTypes: [], taskMaxAttempts: { foo: 5, bar: 2, diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts index 4e138545aec2..ec99c6ad5bf8 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts @@ -202,7 +202,6 @@ export interface UpdateFieldsAndMarkAsFailedOpts { }; claimableTaskTypes: string[]; skippedTaskTypes: string[]; - unusedTaskTypes: string[]; taskMaxAttempts: { [field: string]: number }; } @@ -210,7 +209,6 @@ export const updateFieldsAndMarkAsFailed = ({ fieldUpdates, claimableTaskTypes, skippedTaskTypes, - unusedTaskTypes, taskMaxAttempts, }: UpdateFieldsAndMarkAsFailedOpts): ScriptClause => { const setScheduledAtScript = `if(ctx._source.task.retryAt != null && ZonedDateTime.parse(ctx._source.task.retryAt).toInstant().toEpochMilli() < params.now) { @@ -227,8 +225,6 @@ export const updateFieldsAndMarkAsFailed = ({ source: ` if (params.claimableTaskTypes.contains(ctx._source.task.taskType)) { ${setScheduledAtAndMarkAsClaimed} - } else if (params.unusedTaskTypes.contains(ctx._source.task.taskType)) { - ctx._source.task.status = "unrecognized"; } else { ctx.op = "noop"; }`, @@ -238,7 +234,6 @@ export const updateFieldsAndMarkAsFailed = ({ fieldUpdates, claimableTaskTypes, skippedTaskTypes, - unusedTaskTypes, taskMaxAttempts, }, }; diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts index 437af8e007bd..629e3464399c 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts @@ -83,7 +83,6 @@ describe('TaskClaiming', () => { strategy: 'non-default', definitions, excludedTaskTypes: [], - unusedTypes: [], taskStore: taskStoreMock.create({ taskManagerId: '' }), maxAttempts: 2, getAvailableCapacity: () => 10, @@ -134,7 +133,6 @@ describe('TaskClaiming', () => { strategy: 'default', definitions, excludedTaskTypes: [], - unusedTypes: [], taskStore: taskStoreMock.create({ taskManagerId: '' }), maxAttempts: 2, getAvailableCapacity: () => 10, diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.ts index c9bca3175540..1b1e41490362 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.ts @@ -34,7 +34,6 @@ export interface TaskClaimingOpts { logger: Logger; strategy: string; definitions: TaskTypeDictionary; - unusedTypes: string[]; taskStore: TaskStore; maxAttempts: number; excludedTaskTypes: string[]; @@ -92,7 +91,6 @@ export class TaskClaiming { private readonly taskClaimingBatchesByType: TaskClaimingBatches; private readonly taskMaxAttempts: Record; private readonly excludedTaskTypes: string[]; - private readonly unusedTypes: string[]; private readonly taskClaimer: TaskClaimerFn; private readonly taskPartitioner: TaskPartitioner; @@ -111,7 +109,6 @@ export class TaskClaiming { this.taskClaimingBatchesByType = this.partitionIntoClaimingBatches(this.definitions); this.taskMaxAttempts = Object.fromEntries(this.normalizeMaxAttempts(this.definitions)); this.excludedTaskTypes = opts.excludedTaskTypes; - this.unusedTypes = opts.unusedTypes; this.taskClaimer = getTaskClaimer(this.logger, opts.strategy); this.events$ = new Subject(); this.taskPartitioner = opts.taskPartitioner; @@ -178,7 +175,6 @@ export class TaskClaiming { taskStore: this.taskStore, events$: this.events$, getCapacity: this.getAvailableCapacity, - unusedTypes: this.unusedTypes, definitions: this.definitions, taskMaxAttempts: this.taskMaxAttempts, excludedTaskTypes: this.excludedTaskTypes, diff --git a/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.test.ts b/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.test.ts new file mode 100644 index 000000000000..1485216a67f3 --- /dev/null +++ b/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.test.ts @@ -0,0 +1,266 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mockLogger } from '../test_utils'; +import { coreMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { SCHEDULE_INTERVAL, taskRunner } from './mark_removed_tasks_as_unrecognized'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +const createTaskDoc = (id: string = '1'): SearchHit => ({ + _index: '.kibana_task_manager_9.0.0_001', + _id: `task:${id}`, + _score: 1, + _source: { + references: [], + type: 'task', + updated_at: '2024-11-06T14:17:55.935Z', + task: { + taskType: 'report', + params: '{}', + state: '{"foo":"test"}', + stateVersion: 1, + runAt: '2024-11-06T14:17:55.935Z', + enabled: true, + scheduledAt: '2024-11-06T14:17:55.935Z', + attempts: 0, + status: 'idle', + startedAt: null, + retryAt: null, + ownerId: null, + partition: 211, + }, + }, +}); + +describe('markRemovedTasksAsUnrecognizedTask', () => { + const logger = mockLogger(); + const coreSetup = coreMock.createSetup(); + const esClient = elasticsearchServiceMock.createStart(); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('marks removed tasks as unrecognized', async () => { + esClient.client.asInternalUser.bulk.mockResolvedValue({ + errors: false, + took: 0, + items: [ + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:123', + _version: 2, + result: 'updated', + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 84, + _primary_term: 1, + status: 200, + }, + }, + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:456', + _version: 2, + result: 'updated', + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 84, + _primary_term: 1, + status: 200, + }, + }, + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:789', + _version: 2, + result: 'updated', + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 84, + _primary_term: 1, + status: 200, + }, + }, + ], + }); + + coreSetup.getStartServices.mockResolvedValue([ + { + ...coreMock.createStart(), + elasticsearch: esClient, + }, + {}, + coreMock.createSetup(), + ]); + // @ts-expect-error + esClient.client.asInternalUser.search.mockResponse({ + hits: { hits: [createTaskDoc('123'), createTaskDoc('456'), createTaskDoc('789')], total: 3 }, + }); + + const runner = taskRunner(logger, coreSetup.getStartServices)(); + const result = await runner.run(); + + expect(esClient.client.asInternalUser.bulk).toHaveBeenCalledWith({ + body: [ + { update: { _id: 'task:123' } }, + { doc: { task: { status: 'unrecognized' } } }, + { update: { _id: 'task:456' } }, + { doc: { task: { status: 'unrecognized' } } }, + { update: { _id: 'task:789' } }, + { doc: { task: { status: 'unrecognized' } } }, + ], + index: '.kibana_task_manager', + refresh: false, + }); + + expect(logger.debug).toHaveBeenCalledWith(`Marked 3 removed tasks as unrecognized`); + + expect(result).toEqual({ + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }); + }); + + it('skips update when there are no removed task types', async () => { + coreSetup.getStartServices.mockResolvedValue([ + { + ...coreMock.createStart(), + elasticsearch: esClient, + }, + {}, + coreMock.createSetup(), + ]); + // @ts-expect-error + esClient.client.asInternalUser.search.mockResponse({ + hits: { hits: [], total: 0 }, + }); + + const runner = taskRunner(logger, coreSetup.getStartServices)(); + const result = await runner.run(); + + expect(esClient.client.asInternalUser.bulk).not.toHaveBeenCalled(); + + expect(result).toEqual({ + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }); + }); + + it('schedules the next run even when there is an error', async () => { + coreSetup.getStartServices.mockResolvedValue([ + { + ...coreMock.createStart(), + elasticsearch: esClient, + }, + {}, + coreMock.createSetup(), + ]); + esClient.client.asInternalUser.search.mockRejectedValueOnce(new Error('foo')); + + const runner = taskRunner(logger, coreSetup.getStartServices)(); + const result = await runner.run(); + + expect(esClient.client.asInternalUser.bulk).not.toHaveBeenCalled(); + + expect(logger.error).toHaveBeenCalledWith( + 'Failed to mark removed tasks as unrecognized. Error: foo' + ); + + expect(result).toEqual({ + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }); + }); + + it('handles partial errors from bulk partial update', async () => { + esClient.client.asInternalUser.bulk.mockResolvedValue({ + errors: false, + took: 0, + items: [ + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:123', + _version: 2, + result: 'updated', + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 84, + _primary_term: 1, + status: 200, + }, + }, + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:456', + _version: 2, + result: 'updated', + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 84, + _primary_term: 1, + status: 200, + }, + }, + { + update: { + _index: '.kibana_task_manager_9.0.0_001', + _id: 'task:789', + _version: 2, + error: { + type: 'document_missing_exception', + reason: '[5]: document missing', + index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', + shard: '0', + index: '.kibana_task_manager_9.0.0_001', + }, + status: 404, + }, + }, + ], + }); + + coreSetup.getStartServices.mockResolvedValue([ + { + ...coreMock.createStart(), + elasticsearch: esClient, + }, + {}, + coreMock.createSetup(), + ]); + // @ts-expect-error + esClient.client.asInternalUser.search.mockResponse({ + hits: { hits: [createTaskDoc('123'), createTaskDoc('456'), createTaskDoc('789')], total: 3 }, + }); + + const runner = taskRunner(logger, coreSetup.getStartServices)(); + const result = await runner.run(); + + expect(esClient.client.asInternalUser.bulk).toHaveBeenCalledWith({ + body: [ + { update: { _id: 'task:123' } }, + { doc: { task: { status: 'unrecognized' } } }, + { update: { _id: 'task:456' } }, + { doc: { task: { status: 'unrecognized' } } }, + { update: { _id: 'task:789' } }, + { doc: { task: { status: 'unrecognized' } } }, + ], + index: '.kibana_task_manager', + refresh: false, + }); + expect(logger.warn).toHaveBeenCalledWith( + `Error updating task task:789 to mark as unrecognized - {\"type\":\"document_missing_exception\",\"reason\":\"[5]: document missing\",\"index_uuid\":\"aAsFqTI0Tc2W0LCWgPNrOA\",\"shard\":\"0\",\"index\":\".kibana_task_manager_9.0.0_001\"}` + ); + + expect(logger.debug).toHaveBeenCalledWith(`Marked 2 removed tasks as unrecognized`); + + expect(result).toEqual({ + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.ts b/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.ts new file mode 100644 index 000000000000..e28d5221e72d --- /dev/null +++ b/x-pack/plugins/task_manager/server/removed_tasks/mark_removed_tasks_as_unrecognized.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/logging'; +import { CoreStart } from '@kbn/core-lifecycle-server'; +import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TaskScheduling } from '../task_scheduling'; +import { TaskTypeDictionary } from '../task_type_dictionary'; +import { ConcreteTaskInstance, TaskManagerStartContract } from '..'; +import { TaskStatus } from '../task'; +import { REMOVED_TYPES } from '../task_type_dictionary'; +import { TASK_MANAGER_INDEX } from '../constants'; + +export const TASK_ID = 'mark_removed_tasks_as_unrecognized'; +const TASK_TYPE = `task_manager:${TASK_ID}`; + +export const SCHEDULE_INTERVAL = '1h'; + +export async function scheduleMarkRemovedTasksAsUnrecognizedDefinition( + logger: Logger, + taskScheduling: TaskScheduling +) { + try { + await taskScheduling.ensureScheduled({ + id: TASK_ID, + taskType: TASK_TYPE, + schedule: { interval: SCHEDULE_INTERVAL }, + state: {}, + params: {}, + }); + } catch (e) { + logger.error(`Error scheduling ${TASK_ID} task, received ${e.message}`); + } +} + +export function registerMarkRemovedTasksAsUnrecognizedDefinition( + logger: Logger, + coreStartServices: () => Promise<[CoreStart, TaskManagerStartContract, unknown]>, + taskTypeDictionary: TaskTypeDictionary +) { + taskTypeDictionary.registerTaskDefinitions({ + [TASK_TYPE]: { + title: 'Mark removed tasks as unrecognized', + createTaskRunner: taskRunner(logger, coreStartServices), + }, + }); +} + +export function taskRunner( + logger: Logger, + coreStartServices: () => Promise<[CoreStart, TaskManagerStartContract, unknown]> +) { + return () => { + return { + async run() { + try { + const [{ elasticsearch }] = await coreStartServices(); + const esClient = elasticsearch.client.asInternalUser; + + const removedTasks = await queryForRemovedTasks(esClient); + + if (removedTasks.length > 0) { + await updateTasksToBeUnrecognized(esClient, logger, removedTasks); + } + + return { + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }; + } catch (e) { + logger.error(`Failed to mark removed tasks as unrecognized. Error: ${e.message}`); + return { + state: {}, + schedule: { interval: SCHEDULE_INTERVAL }, + }; + } + }, + }; + }; +} + +async function queryForRemovedTasks( + esClient: ElasticsearchClient +): Promise>> { + const result = await esClient.search({ + index: TASK_MANAGER_INDEX, + body: { + size: 100, + _source: false, + query: { + bool: { + must: [ + { + terms: { + 'task.taskType': REMOVED_TYPES, + }, + }, + ], + }, + }, + }, + }); + + return result.hits.hits; +} + +async function updateTasksToBeUnrecognized( + esClient: ElasticsearchClient, + logger: Logger, + removedTasks: Array> +) { + const bulkBody = []; + for (const task of removedTasks) { + bulkBody.push({ update: { _id: task._id } }); + bulkBody.push({ doc: { task: { status: TaskStatus.Unrecognized } } }); + } + + let removedCount = 0; + try { + const removeResults = await esClient.bulk({ + index: TASK_MANAGER_INDEX, + refresh: false, + body: bulkBody, + }); + for (const removeResult of removeResults.items) { + if (!removeResult.update || !removeResult.update._id) { + logger.warn( + `Error updating task with unknown to mark as unrecognized - malformed response` + ); + } else if (removeResult.update?.error) { + logger.warn( + `Error updating task ${ + removeResult.update._id + } to mark as unrecognized - ${JSON.stringify(removeResult.update.error)}` + ); + } else { + removedCount++; + } + } + logger.debug(`Marked ${removedCount} removed tasks as unrecognized`); + } catch (err) { + // don't worry too much about errors, we'll try again next time + logger.warn(`Error updating tasks to mark as unrecognized: ${err}`); + } +} diff --git a/x-pack/plugins/task_manager/server/task_claimers/index.ts b/x-pack/plugins/task_manager/server/task_claimers/index.ts index 178ebacf68cb..f41c489fd755 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/index.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/index.ts @@ -26,7 +26,6 @@ export interface TaskClaimerOpts { events$: Subject; taskStore: TaskStore; definitions: TaskTypeDictionary; - unusedTypes: string[]; excludedTaskTypes: string[]; taskMaxAttempts: Record; logger: Logger; diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts index fe44ce9e94c6..07dae3c48a39 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts @@ -190,7 +190,6 @@ describe('TaskClaiming', () => { definitions, taskStore: store, excludedTaskTypes, - unusedTypes: unusedTaskTypes, maxAttempts: taskClaimingOpts.maxAttempts ?? 2, getAvailableCapacity: taskClaimingOpts.getAvailableCapacity ?? (() => 10), taskPartitioner, @@ -206,20 +205,17 @@ describe('TaskClaiming', () => { claimingOpts, hits = [generateFakeTasks(1)], excludedTaskTypes = [], - unusedTaskTypes = [], }: { storeOpts: Partial; taskClaimingOpts: Partial; claimingOpts: Omit; hits?: ConcreteTaskInstance[][]; excludedTaskTypes?: string[]; - unusedTaskTypes?: string[]; }) { const { taskClaiming, store } = initialiseTestClaiming({ storeOpts, taskClaimingOpts, excludedTaskTypes, - unusedTaskTypes, hits, }); @@ -355,7 +351,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -378,7 +373,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 3; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 3; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); @@ -440,312 +435,6 @@ describe('TaskClaiming', () => { expect(result.docs.length).toEqual(3); }); - test('should not claim tasks of removed type', async () => { - const store = taskStoreMock.create({ taskManagerId: 'test-test' }); - store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); - - const fetchedTasks = [ - mockInstance({ id: `id-1`, taskType: 'report' }), - mockInstance({ id: `id-2`, taskType: 'report' }), - mockInstance({ id: `id-3`, taskType: 'yawn' }), - ]; - - const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); - store.msearch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); - store.getDocVersions.mockResolvedValueOnce(docLatestVersions); - - store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); - store.bulkPartialUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(getPartialUpdateResult)); - store.bulkPartialUpdate.mockResolvedValueOnce( - [fetchedTasks[0], fetchedTasks[1]].map(getPartialUpdateResult) - ); - - const taskClaiming = new TaskClaiming({ - logger: taskManagerLogger, - strategy: CLAIM_STRATEGY_MGET, - definitions: taskDefinitions, - taskStore: store, - excludedTaskTypes: [], - unusedTypes: ['report'], - maxAttempts: 2, - getAvailableCapacity: () => 10, - taskPartitioner, - }); - - const resultOrErr = await taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ - claimOwnershipUntil: new Date(), - }); - - if (!isOk(resultOrErr)) { - expect(resultOrErr).toBe(undefined); - } - - const result = unwrap(resultOrErr) as ClaimOwnershipResult; - - expect(apm.startTransaction).toHaveBeenCalledWith( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - expect(mockApmTrans.end).toHaveBeenCalledWith('success'); - - expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 2;', - { tags: ['taskClaiming', 'claimAvailableTasksMget'] } - ); - - expect(store.msearch.mock.calls[0][0]?.[0]).toMatchObject({ - size: 40, - seq_no_primary_term: true, - }); - expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); - expect(store.bulkPartialUpdate).toHaveBeenCalledTimes(2); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(1, [ - { - id: fetchedTasks[2].id, - version: fetchedTasks[2].version, - scheduledAt: fetchedTasks[2].runAt, - attempts: 1, - ownerId: 'test-test', - retryAt: new Date('1970-01-01T00:05:30.000Z'), - status: 'running', - startedAt: new Date('1970-01-01T00:00:00.000Z'), - }, - ]); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(2, [ - { - id: fetchedTasks[0].id, - version: fetchedTasks[0].version, - status: 'unrecognized', - }, - { - id: fetchedTasks[1].id, - version: fetchedTasks[1].version, - status: 'unrecognized', - }, - ]); - expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); - - expect(result.stats).toEqual({ - tasksClaimed: 1, - tasksConflicted: 0, - tasksErrors: 0, - tasksUpdated: 1, - tasksLeftUnclaimed: 0, - staleTasks: 0, - }); - expect(result.docs.length).toEqual(1); - }); - - test('should log warning if error updating single removed task as unrecognized', async () => { - const store = taskStoreMock.create({ taskManagerId: 'test-test' }); - store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); - - const fetchedTasks = [ - mockInstance({ id: `id-1`, taskType: 'report' }), - mockInstance({ id: `id-2`, taskType: 'report' }), - mockInstance({ id: `id-3`, taskType: 'yawn' }), - ]; - - const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); - store.msearch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); - store.getDocVersions.mockResolvedValueOnce(docLatestVersions); - - store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); - store.bulkPartialUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(getPartialUpdateResult)); - store.bulkPartialUpdate.mockResolvedValueOnce([ - asOk(fetchedTasks[0]), - asErr({ - type: 'task', - id: fetchedTasks[1].id, - status: 404, - error: { - type: 'document_missing_exception', - reason: '[5]: document missing', - index_uuid: 'aAsFqTI0Tc2W0LCWgPNrOA', - shard: '0', - index: '.kibana_task_manager_8.16.0_001', - }, - }), - ]); - - const taskClaiming = new TaskClaiming({ - logger: taskManagerLogger, - strategy: CLAIM_STRATEGY_MGET, - definitions: taskDefinitions, - taskStore: store, - excludedTaskTypes: [], - unusedTypes: ['report'], - maxAttempts: 2, - getAvailableCapacity: () => 10, - taskPartitioner, - }); - - const resultOrErr = await taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ - claimOwnershipUntil: new Date(), - }); - - if (!isOk(resultOrErr)) { - expect(resultOrErr).toBe(undefined); - } - - const result = unwrap(resultOrErr) as ClaimOwnershipResult; - - expect(apm.startTransaction).toHaveBeenCalledWith( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - expect(mockApmTrans.end).toHaveBeenCalledWith('success'); - - expect(taskManagerLogger.warn).toHaveBeenCalledWith( - 'Error updating task id-2:task to mark as unrecognized during claim: {"type":"document_missing_exception","reason":"[5]: document missing","index_uuid":"aAsFqTI0Tc2W0LCWgPNrOA","shard":"0","index":".kibana_task_manager_8.16.0_001"}', - { tags: ['taskClaiming', 'claimAvailableTasksMget'] } - ); - expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 1;', - { tags: ['taskClaiming', 'claimAvailableTasksMget'] } - ); - - expect(store.msearch.mock.calls[0][0]?.[0]).toMatchObject({ - size: 40, - seq_no_primary_term: true, - }); - expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); - expect(store.bulkPartialUpdate).toHaveBeenCalledTimes(2); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(1, [ - { - id: fetchedTasks[2].id, - version: fetchedTasks[2].version, - scheduledAt: fetchedTasks[2].runAt, - attempts: 1, - ownerId: 'test-test', - retryAt: new Date('1970-01-01T00:05:30.000Z'), - status: 'running', - startedAt: new Date('1970-01-01T00:00:00.000Z'), - }, - ]); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(2, [ - { - id: fetchedTasks[0].id, - version: fetchedTasks[0].version, - status: 'unrecognized', - }, - { - id: fetchedTasks[1].id, - version: fetchedTasks[1].version, - status: 'unrecognized', - }, - ]); - expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); - - expect(result.stats).toEqual({ - tasksClaimed: 1, - tasksConflicted: 0, - tasksErrors: 0, - tasksUpdated: 1, - tasksLeftUnclaimed: 0, - staleTasks: 0, - }); - expect(result.docs.length).toEqual(1); - }); - - test('should log warning if error updating all removed tasks as unrecognized', async () => { - const store = taskStoreMock.create({ taskManagerId: 'test-test' }); - store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); - - const fetchedTasks = [ - mockInstance({ id: `id-1`, taskType: 'report' }), - mockInstance({ id: `id-2`, taskType: 'report' }), - mockInstance({ id: `id-3`, taskType: 'yawn' }), - ]; - - const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); - store.msearch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); - store.getDocVersions.mockResolvedValueOnce(docLatestVersions); - - store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); - store.bulkPartialUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(getPartialUpdateResult)); - store.bulkPartialUpdate.mockRejectedValueOnce(new Error('Oh no')); - - const taskClaiming = new TaskClaiming({ - logger: taskManagerLogger, - strategy: CLAIM_STRATEGY_MGET, - definitions: taskDefinitions, - taskStore: store, - excludedTaskTypes: [], - unusedTypes: ['report'], - maxAttempts: 2, - getAvailableCapacity: () => 10, - taskPartitioner, - }); - - const resultOrErr = await taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ - claimOwnershipUntil: new Date(), - }); - - if (!isOk(resultOrErr)) { - expect(resultOrErr).toBe(undefined); - } - - const result = unwrap(resultOrErr) as ClaimOwnershipResult; - - expect(apm.startTransaction).toHaveBeenCalledWith( - TASK_MANAGER_MARK_AS_CLAIMED, - TASK_MANAGER_TRANSACTION_TYPE - ); - expect(mockApmTrans.end).toHaveBeenCalledWith('success'); - - expect(taskManagerLogger.warn).toHaveBeenCalledWith( - 'Error updating tasks to mark as unrecognized during claim: Error: Oh no', - { tags: ['taskClaiming', 'claimAvailableTasksMget'] } - ); - expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', - { tags: ['taskClaiming', 'claimAvailableTasksMget'] } - ); - - expect(store.msearch.mock.calls[0][0]?.[0]).toMatchObject({ - size: 40, - seq_no_primary_term: true, - }); - expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); - expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); - expect(store.bulkPartialUpdate).toHaveBeenCalledTimes(2); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(1, [ - { - id: fetchedTasks[2].id, - version: fetchedTasks[2].version, - scheduledAt: fetchedTasks[2].runAt, - attempts: 1, - ownerId: 'test-test', - retryAt: new Date('1970-01-01T00:05:30.000Z'), - status: 'running', - startedAt: new Date('1970-01-01T00:00:00.000Z'), - }, - ]); - expect(store.bulkPartialUpdate).toHaveBeenNthCalledWith(2, [ - { - id: fetchedTasks[0].id, - version: fetchedTasks[0].version, - status: 'unrecognized', - }, - { - id: fetchedTasks[1].id, - version: fetchedTasks[1].version, - status: 'unrecognized', - }, - ]); - - expect(result.stats).toEqual({ - tasksClaimed: 1, - tasksConflicted: 0, - tasksErrors: 0, - tasksUpdated: 1, - tasksLeftUnclaimed: 0, - staleTasks: 0, - }); - expect(result.docs.length).toEqual(1); - }); - test('should handle no tasks to claim', async () => { const store = taskStoreMock.create({ taskManagerId: 'test-test' }); store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); @@ -761,7 +450,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -828,7 +516,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -851,7 +538,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); @@ -922,7 +609,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -945,7 +631,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); @@ -1016,7 +702,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1039,7 +724,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 2; stale: 1; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 2; stale: 1; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); @@ -1116,7 +801,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1139,7 +823,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 4; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 4; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); @@ -1248,7 +932,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1271,7 +954,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 1; removed: 0;', + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 1;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); expect(taskManagerLogger.error).toHaveBeenCalledWith( @@ -1377,7 +1060,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1400,7 +1082,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 3; stale: 0; conflicts: 1; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 3; stale: 0; conflicts: 1; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); expect(taskManagerLogger.warn).toHaveBeenCalledWith( @@ -1504,7 +1186,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1619,7 +1300,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1642,7 +1322,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 1; getErrors: 0; removed: 0;', + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 1; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); expect(taskManagerLogger.error).toHaveBeenCalledWith( @@ -1753,7 +1433,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -1776,7 +1455,7 @@ describe('TaskClaiming', () => { expect(mockApmTrans.end).toHaveBeenCalledWith('success'); expect(taskManagerLogger.debug).toHaveBeenCalledWith( - 'task claimer claimed: 3; stale: 0; conflicts: 1; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0; removed: 0;', + 'task claimer claimed: 3; stale: 0; conflicts: 1; missing: 0; capacity reached: 0; updateErrors: 0; getErrors: 0;', { tags: ['taskClaiming', 'claimAvailableTasksMget'] } ); expect(taskManagerLogger.error).not.toHaveBeenCalled(); @@ -1870,7 +1549,6 @@ describe('TaskClaiming', () => { definitions: taskDefinitions, taskStore: store, excludedTaskTypes: [], - unusedTypes: [], maxAttempts: 2, getAvailableCapacity: () => 10, taskPartitioner, @@ -2488,7 +2166,6 @@ describe('TaskClaiming', () => { strategy: CLAIM_STRATEGY_MGET, definitions, excludedTaskTypes: [], - unusedTypes: [], taskStore, maxAttempts: 2, getAvailableCapacity, diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts index 16d9ba5c7fae..431daab8dd2c 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts @@ -57,7 +57,6 @@ interface OwnershipClaimingOpts { claimOwnershipUntil: Date; size: number; taskTypes: Set; - removedTypes: Set; getCapacity: (taskType?: string | undefined) => number; excludedTaskTypePatterns: string[]; taskStore: TaskStore; @@ -90,19 +89,16 @@ export async function claimAvailableTasksMget( async function claimAvailableTasks(opts: TaskClaimerOpts): Promise { const { getCapacity, claimOwnershipUntil, batches, events$, taskStore, taskPartitioner } = opts; - const { definitions, unusedTypes, excludedTaskTypes, taskMaxAttempts } = opts; + const { definitions, excludedTaskTypes, taskMaxAttempts } = opts; const logger = createWrappedLogger({ logger: opts.logger, tags: [claimAvailableTasksMget.name] }); const initialCapacity = getCapacity(); const stopTaskTimer = startTaskTimer(); - const removedTypes = new Set(unusedTypes); // REMOVED_TYPES - // get a list of candidate tasks to claim, with their version info const { docs, versionMap } = await searchAvailableTasks({ definitions, taskTypes: new Set(definitions.getAllTypes()), excludedTaskTypePatterns: excludedTaskTypes, - removedTypes, taskStore, events$, claimOwnershipUntil, @@ -125,18 +121,12 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise `task:${doc.id}`)); - // filter out stale, missing and removed tasks + // filter out stale and missing tasks const currentTasks: ConcreteTaskInstance[] = []; const staleTasks: ConcreteTaskInstance[] = []; const missingTasks: ConcreteTaskInstance[] = []; - const removedTasks: ConcreteTaskInstance[] = []; for (const searchDoc of docs) { - if (removedTypes.has(searchDoc.taskType)) { - removedTasks.push(searchDoc); - continue; - } - const searchVersion = versionMap.get(searchDoc.id); const latestVersion = docLatestVersions.get(`task:${searchDoc.id}`); if (!searchVersion || !latestVersion) { @@ -236,42 +226,8 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise 0) { - const tasksToRemove = Array.from(removedTasks); - const tasksToRemoveUpdates: PartialConcreteTaskInstance[] = []; - for (const task of tasksToRemove) { - tasksToRemoveUpdates.push({ - id: task.id, - status: TaskStatus.Unrecognized, - }); - } - - // don't worry too much about errors, we'll get them next time - try { - const removeResults = await taskStore.bulkPartialUpdate(tasksToRemoveUpdates); - for (const removeResult of removeResults) { - if (isOk(removeResult)) { - removedCount++; - } else { - const { id, type, error } = removeResult.error; - logger.warn( - `Error updating task ${id}:${type} to mark as unrecognized during claim: ${JSON.stringify( - error - )}` - ); - } - } - } catch (err) { - // swallow the error because this is unrelated to the claim cycle - logger.warn(`Error updating tasks to mark as unrecognized during claim: ${err}`); - } - } - // TODO: need a better way to generate stats - const message = `task claimer claimed: ${fullTasksToRun.length}; stale: ${staleTasks.length}; conflicts: ${conflicts}; missing: ${missingTasks.length}; capacity reached: ${leftOverTasks.length}; updateErrors: ${bulkUpdateErrors}; getErrors: ${bulkGetErrors}; removed: ${removedCount};`; + const message = `task claimer claimed: ${fullTasksToRun.length}; stale: ${staleTasks.length}; conflicts: ${conflicts}; missing: ${missingTasks.length}; capacity reached: ${leftOverTasks.length}; updateErrors: ${bulkUpdateErrors}; getErrors: ${bulkGetErrors};`; logger.debug(message); // build results @@ -306,7 +262,6 @@ export const NO_ASSIGNED_PARTITIONS_WARNING_INTERVAL = 60000; async function searchAvailableTasks({ definitions, taskTypes, - removedTypes, excludedTaskTypePatterns, taskStore, getCapacity, @@ -318,7 +273,6 @@ async function searchAvailableTasks({ const claimPartitions = buildClaimPartitions({ types: taskTypes, excludedTaskTypes, - removedTypes, getCapacity, definitions, }); @@ -352,10 +306,7 @@ async function searchAvailableTasks({ // Task must be enabled EnabledTask, // a task type that's not excluded (may be removed or not) - OneOfTaskTypes( - 'task.taskType', - claimPartitions.unlimitedTypes.concat(Array.from(removedTypes)) - ), + OneOfTaskTypes('task.taskType', claimPartitions.unlimitedTypes), // Either a task with idle status and runAt <= now or // status running or claiming with a retryAt <= now. shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt), @@ -407,7 +358,6 @@ async function searchAvailableTasks({ } interface ClaimPartitions { - removedTypes: string[]; unlimitedTypes: string[]; limitedTypes: Map; } @@ -415,30 +365,23 @@ interface ClaimPartitions { interface BuildClaimPartitionsOpts { types: Set; excludedTaskTypes: Set; - removedTypes: Set; getCapacity: (taskType?: string) => number; definitions: TaskTypeDictionary; } function buildClaimPartitions(opts: BuildClaimPartitionsOpts): ClaimPartitions { const result: ClaimPartitions = { - removedTypes: [], unlimitedTypes: [], limitedTypes: new Map(), }; - const { types, excludedTaskTypes, removedTypes, getCapacity, definitions } = opts; + const { types, excludedTaskTypes, getCapacity, definitions } = opts; for (const type of types) { const definition = definitions.get(type); if (definition == null) continue; if (excludedTaskTypes.has(type)) continue; - if (removedTypes.has(type)) { - result.removedTypes.push(type); - continue; - } - if (definition.maxConcurrency == null) { result.unlimitedTypes.push(definition.type); continue; diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.test.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.test.ts index 13e6faf2de0f..623693e71c54 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.test.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.test.ts @@ -99,14 +99,12 @@ describe('TaskClaiming', () => { hits = [generateFakeTasks(1)], versionConflicts = 2, excludedTaskTypes = [], - unusedTaskTypes = [], }: { storeOpts: Partial; taskClaimingOpts: Partial; hits?: ConcreteTaskInstance[][]; versionConflicts?: number; excludedTaskTypes?: string[]; - unusedTaskTypes?: string[]; }) { const definitions = storeOpts.definitions ?? taskDefinitions; const store = taskStoreMock.create({ taskManagerId: storeOpts.taskManagerId }); @@ -136,7 +134,6 @@ describe('TaskClaiming', () => { definitions, taskStore: store, excludedTaskTypes, - unusedTypes: unusedTaskTypes, maxAttempts: taskClaimingOpts.maxAttempts ?? 2, getAvailableCapacity: taskClaimingOpts.getAvailableCapacity ?? (() => 10), taskPartitioner, @@ -153,7 +150,6 @@ describe('TaskClaiming', () => { hits = [generateFakeTasks(1)], versionConflicts = 2, excludedTaskTypes = [], - unusedTaskTypes = [], }: { storeOpts: Partial; taskClaimingOpts: Partial; @@ -161,14 +157,12 @@ describe('TaskClaiming', () => { hits?: ConcreteTaskInstance[][]; versionConflicts?: number; excludedTaskTypes?: string[]; - unusedTaskTypes?: string[]; }) { const getCapacity = taskClaimingOpts.getAvailableCapacity ?? (() => 10); const { taskClaiming, store } = initialiseTestClaiming({ storeOpts, taskClaimingOpts, excludedTaskTypes, - unusedTaskTypes, hits, versionConflicts, }); @@ -471,7 +465,6 @@ if (doc['task.runAt'].size()!=0) { 'anotherLimitedToOne', 'limitedToTwo', ], - unusedTaskTypes: [], taskMaxAttempts: { unlimited: maxAttempts, }, @@ -493,7 +486,6 @@ if (doc['task.runAt'].size()!=0) { 'anotherLimitedToOne', 'limitedToTwo', ], - unusedTaskTypes: [], taskMaxAttempts: { limitedToOne: maxAttempts, }, @@ -640,7 +632,6 @@ if (doc['task.runAt'].size()!=0) { }, taskPartitioner, excludedTaskTypes: [], - unusedTypes: [], }); const resultOrErr = await taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ @@ -848,129 +839,6 @@ if (doc['task.runAt'].size()!=0) { expect(firstCycle).not.toMatchObject(secondCycle); }); - test('it passes any unusedTaskTypes to script', async () => { - const maxAttempts = _.random(2, 43); - const customMaxAttempts = _.random(44, 100); - const taskManagerId = uuidv1(); - const fieldUpdates = { - ownerId: taskManagerId, - retryAt: new Date(Date.now()), - }; - const definitions = new TaskTypeDictionary(mockLogger()); - definitions.registerTaskDefinitions({ - foo: { - title: 'foo', - createTaskRunner: jest.fn(), - }, - bar: { - title: 'bar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - foobar: { - title: 'foobar', - maxAttempts: customMaxAttempts, - createTaskRunner: jest.fn(), - }, - }); - - const { - args: { - updateByQuery: [{ query, script }], - }, - } = await testClaimAvailableTasks({ - storeOpts: { - definitions, - taskManagerId, - }, - taskClaimingOpts: { - maxAttempts, - }, - claimingOpts: { - claimOwnershipUntil: new Date(), - }, - excludedTaskTypes: ['foobar'], - unusedTaskTypes: ['barfoo'], - }); - expect(query).toMatchObject({ - bool: { - must: [ - { - bool: { - must: [ - { - term: { - 'task.enabled': true, - }, - }, - ], - }, - }, - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - }, - }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - ], - filter: [ - { - bool: { - must_not: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - must: { range: { 'task.retryAt': { gt: 'now' } } }, - }, - }, - ], - }, - }, - ], - }, - }); - expect(script).toMatchObject({ - source: expect.any(String), - lang: 'painless', - params: { - fieldUpdates, - claimableTaskTypes: ['foo', 'bar'], - skippedTaskTypes: ['foobar'], - unusedTaskTypes: ['barfoo'], - taskMaxAttempts: { - bar: customMaxAttempts, - foo: maxAttempts, - }, - }, - }); - }); - test('it claims tasks by setting their ownerId, status and retryAt', async () => { const taskManagerId = uuidv1(); const claimOwnershipUntil = new Date(Date.now()); @@ -1356,7 +1224,6 @@ if (doc['task.runAt'].size()!=0) { strategy: 'update_by_query', definitions, excludedTaskTypes: [], - unusedTypes: [], taskStore, maxAttempts: 2, getAvailableCapacity, diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.ts index 5a4bccb43b98..fdfd09e07f9c 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_update_by_query.ts @@ -51,7 +51,6 @@ interface OwnershipClaimingOpts { taskStore: TaskStore; events$: Subject; definitions: TaskTypeDictionary; - unusedTypes: string[]; excludedTaskTypes: string[]; taskMaxAttempts: Record; } @@ -60,7 +59,7 @@ export async function claimAvailableTasksUpdateByQuery( opts: TaskClaimerOpts ): Promise { const { getCapacity, claimOwnershipUntil, batches, events$, taskStore } = opts; - const { definitions, unusedTypes, excludedTaskTypes, taskMaxAttempts } = opts; + const { definitions, excludedTaskTypes, taskMaxAttempts } = opts; const initialCapacity = getCapacity(); let accumulatedResult = getEmptyClaimOwnershipResult(); @@ -83,7 +82,6 @@ export async function claimAvailableTasksUpdateByQuery( taskTypes: isLimited(batch) ? new Set([batch.tasksTypes]) : batch.tasksTypes, taskStore, definitions, - unusedTypes, excludedTaskTypes, taskMaxAttempts, }); @@ -137,7 +135,6 @@ async function markAvailableTasksAsClaimed({ claimOwnershipUntil, size, taskTypes, - unusedTypes, taskMaxAttempts, }: OwnershipClaimingOpts): Promise { const { taskTypesToSkip = [], taskTypesToClaim = [] } = groupBy( @@ -164,7 +161,6 @@ async function markAvailableTasksAsClaimed({ }, claimableTaskTypes: taskTypesToClaim, skippedTaskTypes: taskTypesToSkip, - unusedTaskTypes: unusedTypes, taskMaxAttempts: pick(taskMaxAttempts, taskTypesToClaim), }); diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json index 55ad764c5bdc..b11eaaf44a90 100644 --- a/x-pack/plugins/task_manager/tsconfig.json +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -27,7 +27,8 @@ "@kbn/logging", "@kbn/core-lifecycle-server", "@kbn/cloud-plugin", - "@kbn/core-saved-objects-base-server-internal" + "@kbn/core-saved-objects-base-server-internal", + "@kbn/core-elasticsearch-server", ], "exclude": ["target/**/*"] } diff --git a/x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture/server/plugin.ts b/x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture/server/plugin.ts index 6e4eba065ec7..f6e338327043 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/task_manager_fixture/server/plugin.ts @@ -95,6 +95,34 @@ export class SampleTaskManagerFixturePlugin return res.ok({ body: {} }); } ); + + router.post( + { + path: '/api/alerting_tasks/run_mark_tasks_as_unrecognized', + validate: { + body: schema.object({}), + }, + }, + async ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> => { + try { + const taskManager = await this.taskManagerStart; + await taskManager.ensureScheduled({ + id: 'mark_removed_tasks_as_unrecognized', + taskType: 'task_manager:mark_removed_tasks_as_unrecognized', + schedule: { interval: '1h' }, + state: {}, + params: {}, + }); + return res.ok({ body: await taskManager.runSoon('mark_removed_tasks_as_unrecognized') }); + } catch (err) { + return res.ok({ body: { id: 'mark_removed_tasks_as_unrecognized', error: `${err}` } }); + } + } + ); } public start(core: CoreStart, { taskManager }: SampleTaskManagerFixtureStartDeps) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/scheduled_task_id.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/scheduled_task_id.ts index 0086dd2679c7..f05075be810a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/scheduled_task_id.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/scheduled_task_id.ts @@ -138,6 +138,12 @@ export default function createScheduledTaskIdTests({ getService }: FtrProviderCo // When we enable the rule, the unrecognized task should be removed and a new // task created in its place + await supertestWithoutAuth + .post('/api/alerting_tasks/run_mark_tasks_as_unrecognized') + .set('kbn-xsrf', 'foo') + .send({}) + .expect(200); + // scheduled task should exist and be unrecognized await retry.try(async () => { const taskRecordLoaded = await getScheduledTask(RULE_ID); diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts index 51b0842e60bc..c5927d894911 100644 --- a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts @@ -114,6 +114,34 @@ export function initRoutes( } ); + router.post( + { + path: `/api/sample_tasks/run_mark_removed_tasks_as_unrecognized`, + validate: { + body: schema.object({}), + }, + }, + async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const taskManager = await taskManagerStart; + await taskManager.ensureScheduled({ + id: 'mark_removed_tasks_as_unrecognized', + taskType: 'task_manager:mark_removed_tasks_as_unrecognized', + schedule: { interval: '1h' }, + state: {}, + params: {}, + }); + return res.ok({ body: await taskManager.runSoon('mark_removed_tasks_as_unrecognized') }); + } catch (err) { + return res.ok({ body: { id: 'mark_removed_tasks_as_unrecognized', error: `${err}` } }); + } + } + ); + router.post( { path: `/api/sample_tasks/bulk_enable`, diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index 091e0fe01e41..c8056c2ee205 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -168,6 +168,7 @@ export default function ({ getService }: FtrProviderContext) { 'security:telemetry-timelines', 'session_cleanup', 'task_manager:delete_inactive_background_task_nodes', + 'task_manager:mark_removed_tasks_as_unrecognized', ]); }); }); diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts index aae90a52572c..a7447353e805 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management_removed_types.ts @@ -92,6 +92,12 @@ export default function ({ getService }: FtrProviderContext) { let scheduledTaskRuns = 0; let scheduledTaskInstanceRunAt = scheduledTask.runAt; + await request + .post('/api/sample_tasks/run_mark_removed_tasks_as_unrecognized') + .set('kbn-xsrf', 'xxx') + .send({}) + .expect(200); + await retry.try(async () => { const tasks = (await currentTasks()).docs; expect(tasks.length).to.eql(3); diff --git a/x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget/server/init_routes.ts b/x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget/server/init_routes.ts index f1e697399fe0..acdbae0b0033 100644 --- a/x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget/server/init_routes.ts +++ b/x-pack/test/task_manager_claimer_update_by_query/plugins/sample_task_plugin_mget/server/init_routes.ts @@ -117,6 +117,34 @@ export function initRoutes( } ); + router.post( + { + path: `/api/sample_tasks/run_mark_removed_tasks_as_unrecognized`, + validate: { + body: schema.object({}), + }, + }, + async function ( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const taskManager = await taskManagerStart; + await taskManager.ensureScheduled({ + id: 'mark_removed_tasks_as_unrecognized', + taskType: 'task_manager:mark_removed_tasks_as_unrecognized', + schedule: { interval: '1h' }, + state: {}, + params: {}, + }); + return res.ok({ body: await taskManager.runSoon('mark_removed_tasks_as_unrecognized') }); + } catch (err) { + return res.ok({ body: { id: 'mark_removed_tasks_as_unrecognized', error: `${err}` } }); + } + } + ); + router.post( { path: `/api/sample_tasks/bulk_enable`, diff --git a/x-pack/test/task_manager_claimer_update_by_query/test_suites/task_manager/task_management_removed_types.ts b/x-pack/test/task_manager_claimer_update_by_query/test_suites/task_manager/task_management_removed_types.ts index aae90a52572c..a7447353e805 100644 --- a/x-pack/test/task_manager_claimer_update_by_query/test_suites/task_manager/task_management_removed_types.ts +++ b/x-pack/test/task_manager_claimer_update_by_query/test_suites/task_manager/task_management_removed_types.ts @@ -92,6 +92,12 @@ export default function ({ getService }: FtrProviderContext) { let scheduledTaskRuns = 0; let scheduledTaskInstanceRunAt = scheduledTask.runAt; + await request + .post('/api/sample_tasks/run_mark_removed_tasks_as_unrecognized') + .set('kbn-xsrf', 'xxx') + .send({}) + .expect(200); + await retry.try(async () => { const tasks = (await currentTasks()).docs; expect(tasks.length).to.eql(3); From e6265f204f68fcdc15de7a3ffead8abdd1af6f6d Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 12 Nov 2024 07:34:00 +1100 Subject: [PATCH 027/100] skip failing test suite (#199413) --- .../apis/data_views/fields_for_wildcard_route/response.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/api_integration/apis/data_views/fields_for_wildcard_route/response.ts b/test/api_integration/apis/data_views/fields_for_wildcard_route/response.ts index 2b0fa0dab442..a80ca89ee486 100644 --- a/test/api_integration/apis/data_views/fields_for_wildcard_route/response.ts +++ b/test/api_integration/apis/data_views/fields_for_wildcard_route/response.ts @@ -80,7 +80,8 @@ export default function ({ getService }: FtrProviderContext) { }, ]; - describe('fields_for_wildcard_route response', () => { + // Failing: See https://github.com/elastic/kibana/issues/199413 + describe.skip('fields_for_wildcard_route response', () => { before(() => esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index') ); From 932f0aa6a72c49a4ae79098a7512d996f63d2fc3 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 12 Nov 2024 07:44:15 +1100 Subject: [PATCH 028/100] skip failing test suite (#199701) --- .../fleet_api_integration/apis/epm/install_with_streaming.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts b/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts index 152e3dfd4c69..4fa0e485be2b 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts @@ -36,7 +36,8 @@ export default function (providerContext: FtrProviderContext) { return res?._source?.['epm-packages'] as Installation; }; - describe('Installs a package using stream-based approach', () => { + // Failing: See https://github.com/elastic/kibana/issues/199701 + describe.skip('Installs a package using stream-based approach', () => { skipIfNoDockerRegistry(providerContext); before(async () => { From b781c4eec4381ac5ff6bb695d42afb8c971ed70b Mon Sep 17 00:00:00 2001 From: wajihaparvez Date: Mon, 11 Nov 2024 15:59:39 -0500 Subject: [PATCH 029/100] [Docs] Add section for assigning colors to terms (#199241) ## Summary Added a section with instructions for assigning colors to terms in a table. The technical preview warning will be removed in GA. Rel: https://github.com/elastic/kibana/pull/189895 Closes: [#559](https://github.com/elastic/platform-docs-team/issues/559) --------- Co-authored-by: florent-leborgne --- docs/user/dashboard/lens.asciidoc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/user/dashboard/lens.asciidoc b/docs/user/dashboard/lens.asciidoc index 8623b8c3ca10..3c2a120d167d 100644 --- a/docs/user/dashboard/lens.asciidoc +++ b/docs/user/dashboard/lens.asciidoc @@ -96,13 +96,37 @@ All columns that belong to the same layer pane group are sorted in the table. * *Text alignment* — Aligns the values in the cell to the *Left*, *Center*, or *Right*. +* *Color by value* — Applies color to the cell or text values. To change the color, click the *Edit colors* icon. + * *Hide column* — Hides the column for the field. * *Directly filter on click* — Turns column values into clickable links that allow you to filter or drill down into the data. * *Summary row* — Adds a row that displays the summary value. When specified, allows you to enter a *Summary label*. -* *Color by value* — Applies color to the cell or text values. To change the color, click *Edit*. +[float] +[[assign-colors-to-terms]] +===== Assign colors to terms + +preview::[] + +For term-based metrics, assign a color to each term with color mapping. + +. Create a custom table. + +. In the layer pane, select a *Rows* or *Metrics* field. + +. In the *Color by value* option, select *Cell* or *Text*. + +. Click the *Edit colors* icon. + +. Toggle the button to use the Color Mapping feature. + +. Select a color palette and mode. + +. Click *Add assignment* to assign a color to a specific term, or click *Add all unassigned terms* to assign colors to all terms. Assigning colors to dates is unsupported. + +. Configure color assignments. You can also select whether unassigned terms should be mapped to the selected color palette or a single color. [float] [[drag-and-drop-keyboard-navigation]] From e4298492b5e48338396618d51168ea3e8427c103 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Mon, 11 Nov 2024 15:01:46 -0600 Subject: [PATCH 030/100] [Security Solution] Test plans for prebuilt rule import and export (#191116) ## Summary This PR introduces test plans for both [Prebuilt Rule Import](https://github.com/elastic/kibana/issues/180168) (corresponding [PR](https://github.com/elastic/kibana/pull/190198)) and [Prebuilt Rule Export](https://github.com/elastic/kibana/issues/180167) (corresponding [PR](https://github.com/elastic/kibana/pull/194498)). Import is considerably more complicated as it is calculating new values (for `rule_source`, `immutable`), while the export work is mainly removing existing restrictions (which allowed only custom rules to be exported). --------- Co-authored-by: Elastic Machine --- .../prebuilt_rules/exporting.md | 65 +++++++++ .../prebuilt_rules/importing.md | 127 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/exporting.md create mode 100644 x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/importing.md diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/exporting.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/exporting.md new file mode 100644 index 000000000000..f4cbc66779a8 --- /dev/null +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/exporting.md @@ -0,0 +1,65 @@ +# Prebuilt Rule Export + +This is a test plan for the exporting of prebuilt rules. This feature is an aspect of `Milestone 2` of the [Rule Immutability/Customization](https://github.com/elastic/security-team/issues/1974) epic. + +Status: `in progress`. + +## Useful information + +### Tickets + +- [Rule Immutability/Customization](https://github.com/elastic/security-team/issues/1974) +- [Rule Exporting Feature](https://github.com/elastic/kibana/issues/180167#issue-2227974379) +- [Rule Export API PR](https://github.com/elastic/kibana/pull/194498) + +### Terminology + +- **prebuilt rule**: A rule contained in our `Prebuilt Security Detection Rules` integration in Fleet. +- **custom rule**: A rule defined by the user, which has no relation to the prebuilt rules +- **rule source, or ruleSource**: A field on the rule that defines the rule's categorization + +## Scenarios + +### Core Functionality + +#### Scenario: Exporting prebuilt rule individually +```Gherkin +Given a space with prebuilt rules installed +When the user selects "Export rule" from the "All actions" dropdown on the rule's page +Then the rule should be exported as an NDJSON file +And it should include an "immutable" field with a value of true +And its "ruleSource" "type" should be "external" +And its "ruleSource" "isCustomized" value should depend on whether the rule was customized +``` + +#### Scenario: Exporting prebuilt rules in bulk +```Gherkin +Given a space with prebuilt rules installed +When the user selects prebuilt rules in the alerts table +And chooses "Export" from bulk actions +Then the selected rules should be exported as an NDJSON file +And they should include an "immutable" field with a value of true +And their "ruleSource" "type" should be "external" +And their "ruleSource" "isCustomized" should depend on whether the rule was customized +``` + +#### Scenario: Exporting both prebuilt and custom rules in bulk +```Gherkin +Given a space with prebuilt and custom rules installed +When the user selects prebuilt rules in the alerts table +And chooses "Export" from bulk actions +Then the selected rules should be exported as an NDJSON file +And the prebuilt rules should include an "immutable" field with a value of true +And the custom rules should include an "immutable" field with a value of false +And the prebuilt rules' "ruleSource" "type" should be "external" +And the custom rules' "ruleSource" "type" should be "internal" +``` + +### Error Handling + +#### Scenario: Exporting beyond the export limit +```Gherkin +Given a space with prebuilt and custom rules installed +And the number of rules is greater than the export limit (defaults to 10_000) +Then the request should be rejected as a bad request +``` diff --git a/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/importing.md b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/importing.md new file mode 100644 index 000000000000..0c947d0a52b9 --- /dev/null +++ b/x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules/importing.md @@ -0,0 +1,127 @@ +# Prebuilt Rule Import + +This is a test plan for the importing of prebuilt rules. This feature is an aspect of `Milestone 2` of the [Rule Immutability/Customization](https://github.com/elastic/security-team/issues/1974) epic. + +Status: `in progress`. + +## Useful information + +### Tickets + +- [Rule Immutability/Customization](https://github.com/elastic/security-team/issues/1974) +- [Rule Importing Feature](https://github.com/elastic/kibana/issues/180168) +- [Rule Import API PR](https://github.com/elastic/kibana/pull/190198) + +### Terminology + +- **prebuilt rule**: A rule contained in our `Prebuilt Security Detection Rules` integration in Fleet. +- **custom rule**: A rule defined by the user, which has no relation to the prebuilt rules +- **rule source, or ruleSource**: A field on the rule that defines the rule's categorization + +## Scenarios + +### Core Functionality + +#### Scenario: Importing an unmodified prebuilt rule with a matching rule_id and version + +```Gherkin +Given the import payload contains a prebuilt rule with a matching rule_id and version, identical to the published rule +When the user imports the rule +Then the rule should be created or updated +And the ruleSource type should be "external" +And isCustomized should be false +``` + +#### Scenario: Importing a customized prebuilt rule with a matching rule_id and version + +```Gherkin +Given the import payload contains a prebuilt rule with a matching rule_id and version, modified from the published version +When the user imports the rule +Then the rule should be created or updated +And the ruleSource type should be "external" +And isCustomized should be true +``` + +#### Scenario: Importing a prebuilt rule with a matching rule_id but no matching version + +```Gherkin +Given the import payload contains a prebuilt rule with a matching rule_id but no matching version +When the user imports the rule +Then the rule should be created or updated +And the ruleSource type should be "external" +And isCustomized should be true +``` + +#### Scenario: Importing a prebuilt rule with a non-existent rule_id + +```Gherkin +Given the import payload contains a prebuilt rule with a non-existent rule_id +When the user imports the rule +Then the rule should be created +And the ruleSource type should be "internal" +``` + +#### Scenario: Importing a prebuilt rule without a rule_id field + +```Gherkin +Given the import payload contains a prebuilt rule without a rule_id field +When the user imports the rule +Then the import should be rejected with a message "rule_id field is required" +``` + +#### Scenario: Importing a prebuilt rule with a matching rule_id but missing a version field + +```Gherkin +Given the import payload contains a prebuilt rule without a version field +When the user imports the rule +Then the import should be rejected with a message "version field is required" +``` + +#### Scenario: Importing an existing custom rule missing a version field + +```Gherkin +Given the import payload contains an existing custom rule without a version field +When the user imports the rule +Then the rule should be updated +And the ruleSource type should be "internal" +And the "version" field should be set to the existing rule's "version" +``` + +#### Scenario: Importing a new custom rule missing a version field + +```Gherkin +Given the import payload contains a new custom rule without a version field +When the user imports the rule +Then the rule should be created +And the ruleSource type should be "internal" +And the "version" field should be set to 1 +``` + +#### Scenario: Importing a rule with overwrite flag set to true + +```Gherkin +Given the import payload contains a rule with an existing rule_id +And the overwrite flag is set to true +When the user imports the rule +Then the rule should be overwritten +And the ruleSource type should be calculated based on the rule_id and version +``` + +#### Scenario: Importing a rule with overwrite flag set to false + +```Gherkin +Given the import payload contains a rule with an existing rule_id +And the overwrite flag is set to false +When the user imports the rule +Then the import should be rejected with a message "rule_id already exists" +``` + +#### Scenario: Importing both custom and prebuilt rules + +```Gherkin +Given the import payload contains modified and unmodified, custom and prebuilt rules +When the user imports the rule +Then custom rules should be created or updated, with versions defaulted to 1 +And prebuilt rules should be created or updated, +And prebuilt rules missing versions should be rejected +``` From c97b85d1692e3e5f361fb3158035d5023a673204 Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Tue, 12 Nov 2024 00:23:39 +0100 Subject: [PATCH 031/100] [APM] Migrate `/correlations` to deployment agnostic test (#199276) ## Summary Closes https://github.com/elastic/kibana/issues/198962 Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `correlations` test folder to Deployment-agnostic testing strategy. ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` ## Checks - [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --- .../observability/apm/constants/archiver.ts | 32 +++ .../correlations/failed_transactions.spec.ts | 236 ++++++++++++++++++ .../apm/correlations/field_candidates.spec.ts | 63 +++++ .../correlations/field_value_pairs.spec.ts | 44 ++-- .../observability/apm/correlations/index.ts | 18 ++ .../apm}/correlations/latency.spec.ts | 34 +-- .../apm}/correlations/p_values.spec.ts | 44 ++-- .../significant_correlations.spec.ts | 44 ++-- .../apis/observability/apm/index.ts | 1 + .../fixtures/es_archiver/8.0.0/mappings.json | 38 +-- .../correlations/failed_transactions.spec.ts | 223 ----------------- .../correlations/field_candidates.spec.ts | 57 ----- 12 files changed, 449 insertions(+), 385 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/constants/archiver.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/failed_transactions.spec.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_candidates.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/correlations/field_value_pairs.spec.ts (59%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/correlations/latency.spec.ts (93%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/correlations/p_values.spec.ts (58%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/correlations/significant_correlations.spec.ts (71%) delete mode 100644 x-pack/test/apm_api_integration/tests/correlations/failed_transactions.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/correlations/field_candidates.spec.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/constants/archiver.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/constants/archiver.ts new file mode 100644 index 000000000000..6afc2e9eca63 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/constants/archiver.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +type ArchiveName = + | '8.0.0' + | 'apm_8.0.0' + | 'apm_mappings_only_8.0.0' + | 'infra_metrics_and_apm' + | 'metrics_8.0.0' + | 'ml_8.0.0' + | 'observability_overview' + | 'rum_8.0.0' + | 'rum_test_data'; + +export const ARCHIVER_ROUTES: { [key in ArchiveName]: string } = { + '8.0.0': 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/8.0.0', + 'apm_8.0.0': 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_8.0.0', + 'apm_mappings_only_8.0.0': + 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_mappings_only_8.0.0', + infra_metrics_and_apm: + 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/infra_metrics_and_apm', + 'metrics_8.0.0': 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/metrics_8.0.0', + 'ml_8.0.0': 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0', + observability_overview: + 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/observability_overview', + 'rum_8.0.0': 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_8.0.0', + rum_test_data: 'x-pack/test/apm_api_integration/common/fixtures/es_archiver/rum_test_data', +}; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/failed_transactions.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/failed_transactions.spec.ts new file mode 100644 index 000000000000..549f48009197 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/failed_transactions.spec.ts @@ -0,0 +1,236 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { orderBy } from 'lodash'; +import expect from '@kbn/expect'; +import type { FailedTransactionsCorrelationsResponse } from '@kbn/apm-plugin/common/correlations/failed_transactions_correlations/types'; +import { EVENT_OUTCOME } from '@kbn/apm-plugin/common/es_fields/apm'; +import { EventOutcome } from '@kbn/apm-plugin/common/event_outcome'; +import { LatencyDistributionChartType } from '@kbn/apm-plugin/common/latency_distribution_chart_types'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; + +// These tests go through the full sequence of queries required +// to get the final results for a failed transactions correlation analysis. +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); + // This matches the parameters used for the other tab's queries in `../correlations/*`. + const getOptions = () => ({ + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + kuery: '', + }); + + describe('failed transactions', () => { + describe('without data', () => { + it('handles the empty state', async () => { + const overallDistributionResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', + params: { + body: { + ...getOptions(), + percentileThreshold: 95, + chartType: LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + }); + + expect(overallDistributionResponse.status).to.eql( + 200, + `Expected status to be '200', got '${overallDistributionResponse.status}'` + ); + + const errorDistributionResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', + params: { + body: { + ...getOptions(), + percentileThreshold: 95, + termFilters: [{ fieldName: EVENT_OUTCOME, fieldValue: EventOutcome.failure }], + chartType: LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + }); + + expect(errorDistributionResponse.status).to.eql( + 200, + `Expected status to be '200', got '${errorDistributionResponse.status}'` + ); + + const fieldCandidatesResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/correlations/field_candidates/transactions', + params: { + query: getOptions(), + }, + }); + + expect(fieldCandidatesResponse.status).to.eql( + 200, + `Expected status to be '200', got '${fieldCandidatesResponse.status}'` + ); + + const failedTransactionsCorrelationsResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/correlations/p_values/transactions', + params: { + body: { + ...getOptions(), + fieldCandidates: fieldCandidatesResponse.body?.fieldCandidates, + }, + }, + }); + + expect(failedTransactionsCorrelationsResponse.status).to.eql( + 200, + `Expected status to be '200', got '${failedTransactionsCorrelationsResponse.status}'` + ); + + const finalRawResponse: FailedTransactionsCorrelationsResponse = { + ccsWarning: failedTransactionsCorrelationsResponse.body?.ccsWarning, + percentileThresholdValue: overallDistributionResponse.body?.percentileThresholdValue, + overallHistogram: overallDistributionResponse.body?.overallHistogram, + failedTransactionsCorrelations: + failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations, + }; + + expect(finalRawResponse?.failedTransactionsCorrelations?.length).to.eql( + 0, + `Expected 0 identified correlations, got ${finalRawResponse?.failedTransactionsCorrelations?.length}.` + ); + }); + }); + + describe('with data', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); + + it('runs queries and returns results', async () => { + const overallDistributionResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', + params: { + body: { + ...getOptions(), + percentileThreshold: 95, + chartType: LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + }); + + expect(overallDistributionResponse.status).to.eql( + 200, + `Expected status to be '200', got '${overallDistributionResponse.status}'` + ); + + const errorDistributionResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', + params: { + body: { + ...getOptions(), + percentileThreshold: 95, + termFilters: [{ fieldName: EVENT_OUTCOME, fieldValue: EventOutcome.failure }], + chartType: LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + }); + + expect(errorDistributionResponse.status).to.eql( + 200, + `Expected status to be '200', got '${errorDistributionResponse.status}'` + ); + + const fieldCandidatesResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/correlations/field_candidates/transactions', + params: { + query: getOptions(), + }, + }); + + expect(fieldCandidatesResponse.status).to.eql( + 200, + `Expected status to be '200', got '${fieldCandidatesResponse.status}'` + ); + + const fieldCandidates = fieldCandidatesResponse.body?.fieldCandidates.filter( + (t) => !(t === EVENT_OUTCOME) + ); + + // Identified 80 fieldCandidates. + expect(fieldCandidates.length).to.eql( + 80, + `Expected field candidates length to be '80', got '${fieldCandidates.length}'` + ); + + const failedTransactionsCorrelationsResponse = await apmApiClient.readUser({ + endpoint: 'POST /internal/apm/correlations/p_values/transactions', + params: { + body: { + ...getOptions(), + fieldCandidates, + }, + }, + }); + + expect(failedTransactionsCorrelationsResponse.status).to.eql( + 200, + `Expected status to be '200', got '${failedTransactionsCorrelationsResponse.status}'` + ); + + const fieldsToSample = new Set(); + if ( + failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations.length > 0 + ) { + failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations.forEach( + (d) => { + fieldsToSample.add(d.fieldName); + } + ); + } + + const finalRawResponse: FailedTransactionsCorrelationsResponse = { + ccsWarning: failedTransactionsCorrelationsResponse.body?.ccsWarning, + percentileThresholdValue: overallDistributionResponse.body?.percentileThresholdValue, + overallHistogram: overallDistributionResponse.body?.overallHistogram, + errorHistogram: errorDistributionResponse.body?.overallHistogram, + failedTransactionsCorrelations: + failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations, + }; + + expect(finalRawResponse?.percentileThresholdValue).to.be(1309695.875); + expect(finalRawResponse?.errorHistogram?.length).to.be(101); + expect(finalRawResponse?.overallHistogram?.length).to.be(101); + + expect(finalRawResponse?.failedTransactionsCorrelations?.length).to.eql( + 29, + `Expected 29 identified correlations, got ${finalRawResponse?.failedTransactionsCorrelations?.length}.` + ); + + const sortedCorrelations = orderBy( + finalRawResponse?.failedTransactionsCorrelations, + ['score', 'fieldName', 'fieldValue'], + ['desc', 'asc', 'asc'] + ); + const correlation = sortedCorrelations?.[0]; + + expect(typeof correlation).to.be('object'); + expect(correlation?.doc_count).to.be(31); + expect(correlation?.score).to.be(83.70467673605746); + expect(correlation?.bg_count).to.be(31); + expect(correlation?.fieldName).to.be('transaction.result'); + expect(correlation?.fieldValue).to.be('HTTP 5xx'); + expect(typeof correlation?.pValue).to.be('number'); + expect(typeof correlation?.normalizedScore).to.be('number'); + expect(typeof correlation?.failurePercentage).to.be('number'); + expect(typeof correlation?.successPercentage).to.be('number'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_candidates.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_candidates.spec.ts new file mode 100644 index 000000000000..8db9a7df05fd --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_candidates.spec.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); + + const endpoint = 'GET /internal/apm/correlations/field_candidates/transactions'; + + const getOptions = () => ({ + params: { + query: { + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + kuery: '', + }, + }, + }); + + describe('field candidates', () => { + describe('without data', () => { + it('handles the empty state', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); + + expect(response.status).to.be(200); + // If the source indices are empty, there will be no field candidates + // because of the `include_empty_fields: false` option in the query. + expect(response.body?.fieldCandidates.length).to.be(0); + }); + }); + + describe('with data and default args', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); + + it('returns field candidates', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); + + expect(response.status).to.eql(200); + expect(response.body?.fieldCandidates.length).to.be(81); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/correlations/field_value_pairs.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_value_pairs.spec.ts similarity index 59% rename from x-pack/test/apm_api_integration/tests/correlations/field_value_pairs.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_value_pairs.spec.ts index 4765e83342e5..9fcd438421b6 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/field_value_pairs.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/field_value_pairs.spec.ts @@ -6,11 +6,12 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); const endpoint = 'POST /internal/apm/correlations/field_value_pairs/transactions'; @@ -41,22 +42,27 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, }); - registry.when('field value pairs without data', { config: 'trial', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await apmApiClient.readUser({ - endpoint, - ...getOptions(), - }); + describe('field value pairs', () => { + describe('without data', () => { + it('handles the empty state', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); - expect(response.status).to.be(200); - expect(response.body?.fieldValuePairs.length).to.be(0); + expect(response.status).to.be(200); + expect(response.body?.fieldValuePairs.length).to.be(0); + }); }); - }); - registry.when( - 'field value pairs with data and default args', - { config: 'trial', archives: ['8.0.0'] }, - () => { + describe('with data and default args', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); + it('returns field value pairs', async () => { const response = await apmApiClient.readUser({ endpoint, @@ -66,6 +72,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); expect(response.body?.fieldValuePairs.length).to.be(124); }); - } - ); + }); + }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/index.ts new file mode 100644 index 000000000000..660556edb8d7 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('correlations', () => { + loadTestFile(require.resolve('./failed_transactions.spec.ts')); + loadTestFile(require.resolve('./field_candidates.spec.ts')); + loadTestFile(require.resolve('./field_value_pairs.spec.ts')); + loadTestFile(require.resolve('./latency.spec.ts')); + loadTestFile(require.resolve('./p_values.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/latency.spec.ts similarity index 93% rename from x-pack/test/apm_api_integration/tests/correlations/latency.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/latency.spec.ts index 532613697642..e0080806f6a0 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/latency.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/latency.spec.ts @@ -14,13 +14,14 @@ import type { LatencyCorrelationsResponse, } from '@kbn/apm-plugin/common/correlations/latency_correlations/types'; import { LatencyDistributionChartType } from '@kbn/apm-plugin/common/latency_distribution_chart_types'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; // These tests go through the full sequence of queries required // to get the final results for a latency correlation analysis. -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); // This matches the parameters used for the other tab's queries in `../correlations/*`. const getOptions = () => ({ @@ -30,10 +31,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { kuery: '', }); - registry.when( - 'correlations latency overall without data', - { config: 'trial', archives: [] }, - () => { + describe('latency', () => { + describe('overall without data', () => { it('handles the empty state', async () => { const overallDistributionResponse = await apmApiClient.readUser({ endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', @@ -104,13 +103,16 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(finalRawResponse?.overallHistogram).to.be(undefined); expect(finalRawResponse?.latencyCorrelations?.length).to.be(0); }); - } - ); + }); + + describe('with data and opbeans-node args', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); - registry.when( - 'correlations latency with data and opbeans-node args', - { config: 'trial', archives: ['8.0.0'] }, - () => { // putting this into a single `it` because the responses depend on each other it('runs queries and returns results', async () => { const overallDistributionResponse = await apmApiClient.readUser({ @@ -250,6 +252,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(correlation?.ksTest).to.be(1.9848961005439386e-12); expect(correlation?.histogram?.length).to.be(101); }); - } - ); + }); + }); } diff --git a/x-pack/test/apm_api_integration/tests/correlations/p_values.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/p_values.spec.ts similarity index 58% rename from x-pack/test/apm_api_integration/tests/correlations/p_values.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/p_values.spec.ts index 42a9fdadbb48..ba6e3384cec9 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/p_values.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/p_values.spec.ts @@ -6,11 +6,12 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); const endpoint = 'POST /internal/apm/correlations/p_values/transactions'; @@ -41,22 +42,27 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, }); - registry.when('p values without data', { config: 'trial', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await apmApiClient.readUser({ - endpoint, - ...getOptions(), - }); + describe('p values', () => { + describe('without data', () => { + it('handles the empty state', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); - expect(response.status).to.be(200); - expect(response.body?.failedTransactionsCorrelations.length).to.be(0); + expect(response.status).to.be(200); + expect(response.body?.failedTransactionsCorrelations.length).to.be(0); + }); }); - }); - registry.when( - 'p values with data and default args', - { config: 'trial', archives: ['8.0.0'] }, - () => { + describe('with data and default args', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); + it('returns p values', async () => { const response = await apmApiClient.readUser({ endpoint, @@ -66,6 +72,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); expect(response.body?.failedTransactionsCorrelations.length).to.be(15); }); - } - ); + }); + }); } diff --git a/x-pack/test/apm_api_integration/tests/correlations/significant_correlations.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/significant_correlations.spec.ts similarity index 71% rename from x-pack/test/apm_api_integration/tests/correlations/significant_correlations.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/significant_correlations.spec.ts index d4450c192a02..e1f968d17886 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/significant_correlations.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/correlations/significant_correlations.spec.ts @@ -6,11 +6,12 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ARCHIVER_ROUTES } from '../constants/archiver'; -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const esArchiver = getService('esArchiver'); const endpoint = 'POST /internal/apm/correlations/significant_correlations/transactions'; @@ -65,22 +66,27 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, }); - registry.when('significant correlations without data', { config: 'trial', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await apmApiClient.readUser({ - endpoint, - ...getOptions(), - }); + describe('significant correlations', () => { + describe('without data', () => { + it('handles the empty state', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); - expect(response.status).to.be(200); - expect(response.body?.latencyCorrelations.length).to.be(0); + expect(response.status).to.be(200); + expect(response.body?.latencyCorrelations.length).to.be(0); + }); }); - }); - registry.when( - 'significant correlations with data and default args', - { config: 'trial', archives: ['8.0.0'] }, - () => { + describe('with data and default args', () => { + before(async () => { + await esArchiver.load(ARCHIVER_ROUTES['8.0.0']); + }); + after(async () => { + await esArchiver.unload(ARCHIVER_ROUTES['8.0.0']); + }); + it('returns significant correlations', async () => { const response = await apmApiClient.readUser({ endpoint, @@ -90,6 +96,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); expect(response.body?.latencyCorrelations.length).to.be(7); }); - } - ); + }); + }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index f8c035298447..f1e8fc381a07 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -15,6 +15,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); + loadTestFile(require.resolve('./correlations')); loadTestFile(require.resolve('./entities')); loadTestFile(require.resolve('./cold_start')); }); diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/8.0.0/mappings.json b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/8.0.0/mappings.json index a64e037343bb..c79a4c6b5230 100644 --- a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/8.0.0/mappings.json +++ b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/8.0.0/mappings.json @@ -3786,10 +3786,6 @@ "index": { "auto_expand_replicas": "0-1", "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-error" - }, "mapping": { "total_fields": { "limit": "2000" @@ -4243,8 +4239,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } @@ -8183,10 +8178,6 @@ "index": { "auto_expand_replicas": "0-1", "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-metric" - }, "mapping": { "total_fields": { "limit": "2000" @@ -8640,8 +8631,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } @@ -12871,8 +12861,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } @@ -16653,10 +16642,6 @@ "index": { "auto_expand_replicas": "0-1", "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-profile" - }, "mapping": { "total_fields": { "limit": "2000" @@ -17110,8 +17095,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } @@ -20899,10 +20883,6 @@ "index": { "auto_expand_replicas": "0-1", "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-span" - }, "mapping": { "total_fields": { "limit": "2000" @@ -21356,8 +21336,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } @@ -25242,10 +25221,6 @@ "index": { "auto_expand_replicas": "0-1", "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-transaction" - }, "mapping": { "total_fields": { "limit": "2000" @@ -25699,8 +25674,7 @@ "transaction.message.queue.name", "fields.*" ] - }, - "refresh_interval": "1ms" + } } } } diff --git a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.spec.ts b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.spec.ts deleted file mode 100644 index 13754f6c7eb5..000000000000 --- a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.spec.ts +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { orderBy } from 'lodash'; -import expect from '@kbn/expect'; -import type { FailedTransactionsCorrelationsResponse } from '@kbn/apm-plugin/common/correlations/failed_transactions_correlations/types'; -import { EVENT_OUTCOME } from '@kbn/apm-plugin/common/es_fields/apm'; -import { EventOutcome } from '@kbn/apm-plugin/common/event_outcome'; -import { LatencyDistributionChartType } from '@kbn/apm-plugin/common/latency_distribution_chart_types'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -// These tests go through the full sequence of queries required -// to get the final results for a failed transactions correlation analysis. -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - - // This matches the parameters used for the other tab's queries in `../correlations/*`. - const getOptions = () => ({ - environment: 'ENVIRONMENT_ALL', - start: '2020', - end: '2021', - kuery: '', - }); - - registry.when('failed transactions without data', { config: 'trial', archives: [] }, () => { - it('handles the empty state', async () => { - const overallDistributionResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', - params: { - body: { - ...getOptions(), - percentileThreshold: 95, - chartType: LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - }); - - expect(overallDistributionResponse.status).to.eql( - 200, - `Expected status to be '200', got '${overallDistributionResponse.status}'` - ); - - const errorDistributionResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', - params: { - body: { - ...getOptions(), - percentileThreshold: 95, - termFilters: [{ fieldName: EVENT_OUTCOME, fieldValue: EventOutcome.failure }], - chartType: LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - }); - - expect(errorDistributionResponse.status).to.eql( - 200, - `Expected status to be '200', got '${errorDistributionResponse.status}'` - ); - - const fieldCandidatesResponse = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/correlations/field_candidates/transactions', - params: { - query: getOptions(), - }, - }); - - expect(fieldCandidatesResponse.status).to.eql( - 200, - `Expected status to be '200', got '${fieldCandidatesResponse.status}'` - ); - - const failedTransactionsCorrelationsResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/correlations/p_values/transactions', - params: { - body: { - ...getOptions(), - fieldCandidates: fieldCandidatesResponse.body?.fieldCandidates, - }, - }, - }); - - expect(failedTransactionsCorrelationsResponse.status).to.eql( - 200, - `Expected status to be '200', got '${failedTransactionsCorrelationsResponse.status}'` - ); - - const finalRawResponse: FailedTransactionsCorrelationsResponse = { - ccsWarning: failedTransactionsCorrelationsResponse.body?.ccsWarning, - percentileThresholdValue: overallDistributionResponse.body?.percentileThresholdValue, - overallHistogram: overallDistributionResponse.body?.overallHistogram, - failedTransactionsCorrelations: - failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations, - }; - - expect(finalRawResponse?.failedTransactionsCorrelations?.length).to.eql( - 0, - `Expected 0 identified correlations, got ${finalRawResponse?.failedTransactionsCorrelations?.length}.` - ); - }); - }); - - registry.when('failed transactions with data', { config: 'trial', archives: ['8.0.0'] }, () => { - it('runs queries and returns results', async () => { - const overallDistributionResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', - params: { - body: { - ...getOptions(), - percentileThreshold: 95, - chartType: LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - }); - - expect(overallDistributionResponse.status).to.eql( - 200, - `Expected status to be '200', got '${overallDistributionResponse.status}'` - ); - - const errorDistributionResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/latency/overall_distribution/transactions', - params: { - body: { - ...getOptions(), - percentileThreshold: 95, - termFilters: [{ fieldName: EVENT_OUTCOME, fieldValue: EventOutcome.failure }], - chartType: LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - }); - - expect(errorDistributionResponse.status).to.eql( - 200, - `Expected status to be '200', got '${errorDistributionResponse.status}'` - ); - - const fieldCandidatesResponse = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/correlations/field_candidates/transactions', - params: { - query: getOptions(), - }, - }); - - expect(fieldCandidatesResponse.status).to.eql( - 200, - `Expected status to be '200', got '${fieldCandidatesResponse.status}'` - ); - - const fieldCandidates = fieldCandidatesResponse.body?.fieldCandidates.filter( - (t) => !(t === EVENT_OUTCOME) - ); - - // Identified 80 fieldCandidates. - expect(fieldCandidates.length).to.eql( - 80, - `Expected field candidates length to be '80', got '${fieldCandidates.length}'` - ); - - const failedTransactionsCorrelationsResponse = await apmApiClient.readUser({ - endpoint: 'POST /internal/apm/correlations/p_values/transactions', - params: { - body: { - ...getOptions(), - fieldCandidates, - }, - }, - }); - - expect(failedTransactionsCorrelationsResponse.status).to.eql( - 200, - `Expected status to be '200', got '${failedTransactionsCorrelationsResponse.status}'` - ); - - const fieldsToSample = new Set(); - if (failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations.length > 0) { - failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations.forEach((d) => { - fieldsToSample.add(d.fieldName); - }); - } - - const finalRawResponse: FailedTransactionsCorrelationsResponse = { - ccsWarning: failedTransactionsCorrelationsResponse.body?.ccsWarning, - percentileThresholdValue: overallDistributionResponse.body?.percentileThresholdValue, - overallHistogram: overallDistributionResponse.body?.overallHistogram, - errorHistogram: errorDistributionResponse.body?.overallHistogram, - failedTransactionsCorrelations: - failedTransactionsCorrelationsResponse.body?.failedTransactionsCorrelations, - }; - - expect(finalRawResponse?.percentileThresholdValue).to.be(1309695.875); - expect(finalRawResponse?.errorHistogram?.length).to.be(101); - expect(finalRawResponse?.overallHistogram?.length).to.be(101); - - expect(finalRawResponse?.failedTransactionsCorrelations?.length).to.eql( - 29, - `Expected 29 identified correlations, got ${finalRawResponse?.failedTransactionsCorrelations?.length}.` - ); - - const sortedCorrelations = orderBy( - finalRawResponse?.failedTransactionsCorrelations, - ['score', 'fieldName', 'fieldValue'], - ['desc', 'asc', 'asc'] - ); - const correlation = sortedCorrelations?.[0]; - - expect(typeof correlation).to.be('object'); - expect(correlation?.doc_count).to.be(31); - expect(correlation?.score).to.be(83.70467673605746); - expect(correlation?.bg_count).to.be(31); - expect(correlation?.fieldName).to.be('transaction.result'); - expect(correlation?.fieldValue).to.be('HTTP 5xx'); - expect(typeof correlation?.pValue).to.be('number'); - expect(typeof correlation?.normalizedScore).to.be('number'); - expect(typeof correlation?.failurePercentage).to.be('number'); - expect(typeof correlation?.successPercentage).to.be('number'); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/correlations/field_candidates.spec.ts b/x-pack/test/apm_api_integration/tests/correlations/field_candidates.spec.ts deleted file mode 100644 index 4a5472cf5cb2..000000000000 --- a/x-pack/test/apm_api_integration/tests/correlations/field_candidates.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const apmApiClient = getService('apmApiClient'); - const registry = getService('registry'); - - const endpoint = 'GET /internal/apm/correlations/field_candidates/transactions'; - - const getOptions = () => ({ - params: { - query: { - environment: 'ENVIRONMENT_ALL', - start: '2020', - end: '2021', - kuery: '', - }, - }, - }); - - registry.when('field candidates without data', { config: 'trial', archives: [] }, () => { - it('handles the empty state', async () => { - const response = await apmApiClient.readUser({ - endpoint, - ...getOptions(), - }); - - expect(response.status).to.be(200); - // If the source indices are empty, there will be no field candidates - // because of the `include_empty_fields: false` option in the query. - expect(response.body?.fieldCandidates.length).to.be(0); - }); - }); - - registry.when( - 'field candidates with data and default args', - { config: 'trial', archives: ['8.0.0'] }, - () => { - it('returns field candidates', async () => { - const response = await apmApiClient.readUser({ - endpoint, - ...getOptions(), - }); - - expect(response.status).to.eql(200); - expect(response.body?.fieldCandidates.length).to.be(81); - }); - } - ); -} From f54b95179fa7af0ff952afd6b3ac7b11f1178804 Mon Sep 17 00:00:00 2001 From: "elastic-renovate-prod[bot]" <174716857+elastic-renovate-prod[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:28:14 -0600 Subject: [PATCH 032/100] Update dependency @redocly/cli to ^1.25.11 (main) (#199647) --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 4679049714d1..0ad1d96d7b5d 100644 --- a/package.json +++ b/package.json @@ -1498,7 +1498,7 @@ "@octokit/rest": "^17.11.2", "@parcel/watcher": "^2.1.0", "@playwright/test": "=1.46.0", - "@redocly/cli": "^1.25.10", + "@redocly/cli": "^1.25.11", "@statoscope/webpack-plugin": "^5.28.2", "@storybook/addon-a11y": "^6.5.16", "@storybook/addon-actions": "^6.5.16", diff --git a/yarn.lock b/yarn.lock index b4023f405d87..47ee0df93099 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8483,12 +8483,12 @@ require-from-string "^2.0.2" uri-js-replace "^1.0.1" -"@redocly/cli@^1.25.10": - version "1.25.10" - resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.25.10.tgz#647e33e4171d74a4f879304ba87366ac650ed83d" - integrity sha512-zoRMvSYOLzurcb3be5HLLlc5dLGICyHY8mueCbdE2DmLbFERhJJ5iiABKvNRJSr03AR6X569f4mraBJpAsGJnQ== +"@redocly/cli@^1.25.11": + version "1.25.11" + resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.25.11.tgz#8ec17a6535aebfd166e8cab8ffcc9d768af1b014" + integrity sha512-dttBsmLnnbTlJCTa+s7Sy+qtXDq692n7Ru3nUUIHp9XdCbhXIHWhpc8uAl+GmR4MGbVe8ohATl3J+zX3aFy82A== dependencies: - "@redocly/openapi-core" "1.25.10" + "@redocly/openapi-core" "1.25.11" abort-controller "^3.0.0" chokidar "^3.5.1" colorette "^1.2.0" @@ -8513,10 +8513,10 @@ resolved "https://registry.yarnpkg.com/@redocly/config/-/config-0.16.0.tgz#4b7700a5cb6e04bc6d6fdb94b871c9e260a1fba6" integrity sha512-t9jnODbUcuANRSl/K4L9nb12V+U5acIHnVSl26NWrtSdDZVtoqUXk2yGFPZzohYf62cCfEQUT8ouJ3bhPfpnJg== -"@redocly/openapi-core@1.25.10", "@redocly/openapi-core@^1.4.0": - version "1.25.10" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.25.10.tgz#6ca3f1ad1b826e3680f91752abf11aa40856f6b8" - integrity sha512-wcGnSonJZvjpPaJJs+qh0ADYy0aCbaNhCXhJVES9RlknMc7V9nbqLQ67lkwaXhpp/fskm9GJWL/U9Xyiuclbqw== +"@redocly/openapi-core@1.25.11", "@redocly/openapi-core@^1.4.0": + version "1.25.11" + resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.25.11.tgz#93f168284986da6809363b001e9aa7c2104c2fc0" + integrity sha512-bH+a8izQz4fnKROKoX3bEU8sQ9rjvEIZOqU6qTmxlhOJ0NsKa5e+LmU18SV0oFeg5YhWQhhEDihXkvKJ1wMMNQ== dependencies: "@redocly/ajv" "^8.11.2" "@redocly/config" "^0.16.0" From 1127bf491d78b7ce2319a13257dbf0b1e84226e8 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 11 Nov 2024 17:10:07 -0700 Subject: [PATCH 033/100] [Security solution] Knowledge base entry telemetry (#199225) --- .../server/tracers/telemetry/index.ts | 8 + .../telemetry/telemetry_tracer.test.ts | 204 ++++++++++++++++++ .../tracers/telemetry/telemetry_tracer.ts | 94 ++++++++ .../create_knowledge_base_entry.ts | 28 ++- .../knowledge_base/index.ts | 7 +- .../server/lib/langchain/executors/types.ts | 4 + .../graphs/default_assistant_graph/helpers.ts | 25 ++- .../graphs/default_assistant_graph/index.ts | 18 +- .../lib/telemetry/event_based_telemetry.ts | 152 +++++++++++++ .../server/routes/evaluate/post_evaluate.ts | 1 + .../server/routes/helpers.ts | 19 +- .../entries/bulk_actions_route.ts | 36 +++- .../knowledge_base/entries/create_route.ts | 1 + .../plugins/elastic_assistant/server/types.ts | 1 + .../plugins/elastic_assistant/tsconfig.json | 3 +- .../knowledge_base_write_tool.ts | 6 +- 16 files changed, 578 insertions(+), 29 deletions(-) create mode 100644 x-pack/packages/kbn-langchain/server/tracers/telemetry/index.ts create mode 100644 x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts create mode 100644 x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts diff --git a/x-pack/packages/kbn-langchain/server/tracers/telemetry/index.ts b/x-pack/packages/kbn-langchain/server/tracers/telemetry/index.ts new file mode 100644 index 000000000000..079c0e9a3308 --- /dev/null +++ b/x-pack/packages/kbn-langchain/server/tracers/telemetry/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { TelemetryTracer } from './telemetry_tracer'; diff --git a/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts b/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts new file mode 100644 index 000000000000..bca293ba9957 --- /dev/null +++ b/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AnalyticsServiceSetup, Logger } from '@kbn/core/server'; +import { TelemetryTracer, TelemetryParams } from './telemetry_tracer'; +import { Run } from 'langsmith/schemas'; +import { loggerMock } from '@kbn/logging-mocks'; + +const mockRun = { + inputs: { + responseLanguage: 'English', + conversationId: 'db8f74c5-7dca-43a3-b592-d56f219dffab', + llmType: 'openai', + isStream: false, + isOssModel: false, + }, + outputs: { + input: + 'Generate an ESQL query to find documents with `host.name` that contains my favorite color', + lastNode: 'agent', + steps: [ + { + action: { + tool: 'KnowledgeBaseRetrievalTool', + toolInput: { + query: "user's favorite color", + }, + }, + observation: + '"[{\\"pageContent\\":\\"favorite color is blue\\",\\"metadata\\":{\\"source\\":\\"conversation\\",\\"required\\":false,\\"kbResource\\":\\"user\\"}},{\\"pageContent\\":\\"favorite food is pizza\\",\\"metadata\\":{\\"source\\":\\"conversation\\",\\"required\\":false,\\"kbResource\\":\\"user\\"}}]"', + }, + { + action: { + tool: 'NaturalLanguageESQLTool', + toolInput: { + question: 'Generate an ESQL query to find documents with host.name that contains blue', + }, + }, + observation: + '"To find documents with `host.name` that contains \\"blue\\", you can use the `LIKE` operator with wildcards. Here is the ES|QL query:\\n\\n```esql\\nFROM your_index\\n| WHERE host.name LIKE \\"*blue*\\"\\n```\\n\\nReplace `your_index` with the actual name of your index. This query will filter documents where the `host.name` field contains the substring \\"blue\\"."', + }, + { + action: { + tool: 'KnowledgeBaseRetrievalTool', + toolInput: { + query: "user's favorite food", + }, + }, + observation: + '"[{\\"pageContent\\":\\"favorite color is blue\\",\\"metadata\\":{\\"source\\":\\"conversation\\",\\"required\\":false,\\"kbResource\\":\\"user\\"}},{\\"pageContent\\":\\"favorite food is pizza\\",\\"metadata\\":{\\"source\\":\\"conversation\\",\\"required\\":false,\\"kbResource\\":\\"user\\"}}]"', + }, + { + action: { + tool: 'CustomIndexTool', + toolInput: { + query: 'query about index', + }, + }, + observation: '"Wow this is totally cool."', + }, + { + action: { + tool: 'CustomIndexTool', + toolInput: { + query: 'query about index', + }, + }, + observation: '"Wow this is totally cool."', + }, + { + action: { + tool: 'CustomIndexTool', + toolInput: { + query: 'query about index', + }, + }, + observation: '"Wow this is totally cool."', + }, + ], + hasRespondStep: false, + agentOutcome: { + returnValues: { + output: + 'To find documents with `host.name` that contains your favorite color "blue", you can use the `LIKE` operator with wildcards. Here is the ES|QL query:\n\n```esql\nFROM your_index\n| WHERE host.name LIKE "*blue*"\n```\n\nReplace `your_index` with the actual name of your index. This query will filter documents where the `host.name` field contains the substring "blue".', + }, + log: 'To find documents with `host.name` that contains your favorite color "blue", you can use the `LIKE` operator with wildcards. Here is the ES|QL query:\n\n```esql\nFROM your_index\n| WHERE host.name LIKE "*blue*"\n```\n\nReplace `your_index` with the actual name of your index. This query will filter documents where the `host.name` field contains the substring "blue".', + }, + messages: [], + chatTitle: 'Welcome', + llmType: 'openai', + isStream: false, + isOssModel: false, + conversation: { + timestamp: '2024-11-07T17:37:07.400Z', + createdAt: '2024-11-07T17:37:07.400Z', + users: [ + { + id: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0', + name: 'elastic', + }, + ], + title: 'Welcome', + category: 'assistant', + apiConfig: { + connectorId: 'my-gpt4o-ai', + actionTypeId: '.gen-ai', + }, + isDefault: true, + messages: [ + { + timestamp: '2024-11-07T22:47:45.994Z', + content: + 'Generate an ESQL query to find documents with `host.name` that contains my favorite color', + role: 'user', + }, + ], + updatedAt: '2024-11-08T17:01:21.958Z', + replacements: {}, + namespace: 'default', + id: 'db8f74c5-7dca-43a3-b592-d56f219dffab', + }, + conversationId: 'db8f74c5-7dca-43a3-b592-d56f219dffab', + responseLanguage: 'English', + }, + end_time: 1731085297190, + start_time: 1731085289113, +} as unknown as Run; +const elasticTools = [ + 'AlertCountsTool', + 'NaturalLanguageESQLTool', + 'KnowledgeBaseRetrievalTool', + 'KnowledgeBaseWriteTool', + 'OpenAndAcknowledgedAlertsTool', + 'SecurityLabsKnowledgeBaseTool', +]; +const mockLogger = loggerMock.create(); + +describe('TelemetryTracer', () => { + let telemetry: AnalyticsServiceSetup; + let logger: Logger; + let telemetryParams: TelemetryParams; + let telemetryTracer: TelemetryTracer; + const reportEvent = jest.fn(); + beforeEach(() => { + telemetry = { + reportEvent, + } as unknown as AnalyticsServiceSetup; + logger = mockLogger; + telemetryParams = { + eventType: 'INVOKE_AI_SUCCESS', + assistantStreamingEnabled: true, + actionTypeId: '.gen-ai', + isEnabledKnowledgeBase: true, + model: 'test_model', + }; + telemetryTracer = new TelemetryTracer( + { + elasticTools, + telemetry, + telemetryParams, + totalTools: 9, + }, + logger + ); + }); + + it('should initialize correctly', () => { + expect(telemetryTracer.name).toBe('telemetry_tracer'); + expect(telemetryTracer.elasticTools).toEqual(elasticTools); + expect(telemetryTracer.telemetry).toBe(telemetry); + expect(telemetryTracer.telemetryParams).toBe(telemetryParams); + expect(telemetryTracer.totalTools).toBe(9); + }); + + it('should not log and report event on chain end if parent_run_id exists', async () => { + await telemetryTracer.onChainEnd({ ...mockRun, parent_run_id: '123' }); + + expect(logger.get().debug).not.toHaveBeenCalled(); + expect(telemetry.reportEvent).not.toHaveBeenCalled(); + }); + + it('should log and report event on chain end', async () => { + await telemetryTracer.onChainEnd(mockRun); + + expect(logger.get().debug).toHaveBeenCalledWith(expect.any(Function)); + expect(telemetry.reportEvent).toHaveBeenCalledWith('INVOKE_AI_SUCCESS', { + assistantStreamingEnabled: true, + actionTypeId: '.gen-ai', + isEnabledKnowledgeBase: true, + model: 'test_model', + isOssModel: false, + durationMs: 8077, + toolsInvoked: { + KnowledgeBaseRetrievalTool: 2, + NaturalLanguageESQLTool: 1, + CustomTool: 3, + }, + }); + }); +}); diff --git a/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts b/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts new file mode 100644 index 000000000000..7031e638c1fa --- /dev/null +++ b/x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { BaseCallbackHandlerInput } from '@langchain/core/callbacks/base'; +import type { Run } from 'langsmith/schemas'; +import { BaseTracer } from '@langchain/core/tracers/base'; +import { AnalyticsServiceSetup, Logger } from '@kbn/core/server'; + +export interface TelemetryParams { + assistantStreamingEnabled: boolean; + actionTypeId: string; + isEnabledKnowledgeBase: boolean; + eventType: string; + model?: string; +} +export interface LangChainTracerFields extends BaseCallbackHandlerInput { + elasticTools: string[]; + telemetry: AnalyticsServiceSetup; + telemetryParams: TelemetryParams; + totalTools: number; +} +interface ToolRunStep { + action: { + tool: string; + }; +} +/** + * TelemetryTracer is a tracer that uses event based telemetry to track LangChain events. + */ +export class TelemetryTracer extends BaseTracer implements LangChainTracerFields { + name = 'telemetry_tracer'; + logger: Logger; + elasticTools: string[]; + telemetry: AnalyticsServiceSetup; + telemetryParams: TelemetryParams; + totalTools: number; + constructor(fields: LangChainTracerFields, logger: Logger) { + super(fields); + this.logger = logger.get('telemetryTracer'); + this.elasticTools = fields.elasticTools; + this.telemetry = fields.telemetry; + this.telemetryParams = fields.telemetryParams; + this.totalTools = fields.totalTools; + } + + async onChainEnd(run: Run): Promise { + if (!run.parent_run_id) { + const { eventType, ...telemetryParams } = this.telemetryParams; + const toolsInvoked = + run?.outputs && run?.outputs.steps.length + ? run.outputs.steps.reduce((acc: { [k: string]: number }, event: ToolRunStep | never) => { + if ('action' in event && event?.action?.tool) { + if (this.elasticTools.includes(event.action.tool)) { + return { + ...acc, + ...(event.action.tool in acc + ? { [event.action.tool]: acc[event.action.tool] + 1 } + : { [event.action.tool]: 1 }), + }; + } else { + // Custom tool names are user data, so we strip them out + return { + ...acc, + ...('CustomTool' in acc + ? { CustomTool: acc.CustomTool + 1 } + : { CustomTool: 1 }), + }; + } + } + return acc; + }, {}) + : {}; + const telemetryValue = { + ...telemetryParams, + durationMs: (run.end_time ?? 0) - (run.start_time ?? 0), + toolsInvoked, + ...(telemetryParams.actionTypeId === '.gen-ai' + ? { isOssModel: run.inputs.isOssModel } + : {}), + }; + this.logger.debug( + () => `Invoke ${eventType} telemetry:\n${JSON.stringify(telemetryValue, null, 2)}` + ); + this.telemetry.reportEvent(eventType, telemetryValue); + } + } + + // everything below is required for type only + protected async persistRun(_run: Run): Promise {} +} diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts index 09bb5b291ef9..77a1e37df965 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts @@ -6,7 +6,12 @@ */ import { v4 as uuidv4 } from 'uuid'; -import { AuthenticatedUser, ElasticsearchClient, Logger } from '@kbn/core/server'; +import { + AnalyticsServiceSetup, + AuthenticatedUser, + ElasticsearchClient, + Logger, +} from '@kbn/core/server'; import { DocumentEntryCreateFields, @@ -15,6 +20,10 @@ import { KnowledgeBaseEntryUpdateProps, Metadata, } from '@kbn/elastic-assistant-common'; +import { + CREATE_KNOWLEDGE_BASE_ENTRY_ERROR_EVENT, + CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT, +} from '../../lib/telemetry/event_based_telemetry'; import { getKnowledgeBaseEntry } from './get_knowledge_base_entry'; import { CreateKnowledgeBaseEntrySchema, UpdateKnowledgeBaseEntrySchema } from './types'; @@ -27,6 +36,7 @@ export interface CreateKnowledgeBaseEntryParams { knowledgeBaseEntry: KnowledgeBaseEntryCreateProps | LegacyKnowledgeBaseEntryCreateProps; global?: boolean; isV2?: boolean; + telemetry: AnalyticsServiceSetup; } export const createKnowledgeBaseEntry = async ({ @@ -38,6 +48,7 @@ export const createKnowledgeBaseEntry = async ({ logger, global = false, isV2 = false, + telemetry, }: CreateKnowledgeBaseEntryParams): Promise => { const createdAt = new Date().toISOString(); const body = isV2 @@ -55,6 +66,12 @@ export const createKnowledgeBaseEntry = async ({ entry: knowledgeBaseEntry as unknown as TransformToLegacyCreateSchemaProps['entry'], global, }); + const telemetryPayload = { + entryType: body.type, + required: body.required ?? false, + sharing: body.users.length ? 'private' : 'global', + ...(body.type === 'document' ? { source: body.source } : {}), + }; try { const response = await esClient.create({ body, @@ -63,17 +80,24 @@ export const createKnowledgeBaseEntry = async ({ refresh: 'wait_for', }); - return await getKnowledgeBaseEntry({ + const newKnowledgeBaseEntry = await getKnowledgeBaseEntry({ esClient, knowledgeBaseIndex, id: response._id, logger, user, }); + + telemetry.reportEvent(CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT.eventType, telemetryPayload); + return newKnowledgeBaseEntry; } catch (err) { logger.error( `Error creating Knowledge Base Entry: ${err} with kbResource: ${knowledgeBaseEntry.name}` ); + telemetry.reportEvent(CREATE_KNOWLEDGE_BASE_ENTRY_ERROR_EVENT.eventType, { + ...telemetryPayload, + errorMessage: err.message ?? 'Unknown error', + }); throw err; } }; diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts index 333fbb796ddd..50e124321fe6 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts @@ -25,7 +25,7 @@ import { import pRetry from 'p-retry'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { StructuredTool } from '@langchain/core/tools'; -import { ElasticsearchClient } from '@kbn/core/server'; +import { AnalyticsServiceSetup, ElasticsearchClient } from '@kbn/core/server'; import { IndexPatternsFetcher } from '@kbn/data-views-plugin/server'; import { map } from 'lodash'; import { AIAssistantDataClient, AIAssistantDataClientParams } from '..'; @@ -459,6 +459,8 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { filter: docsCreated.map((c) => `_id:${c}`).join(' OR '), }) : undefined; + // Intentionally no telemetry here - this path only used to install security docs + // Plans to make this function private in a different PR so no user entry ever is created in this path this.options.logger.debug(`created: ${created?.data.hits.hits.length ?? '0'}`); this.options.logger.debug(() => `errors: ${JSON.stringify(errors, null, 2)}`); @@ -686,10 +688,12 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { */ public createKnowledgeBaseEntry = async ({ knowledgeBaseEntry, + telemetry, global = false, }: { knowledgeBaseEntry: KnowledgeBaseEntryCreateProps | LegacyKnowledgeBaseEntryCreateProps; global?: boolean; + telemetry: AnalyticsServiceSetup; }): Promise => { const authenticatedUser = this.options.currentUser; @@ -716,6 +720,7 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { user: authenticatedUser, knowledgeBaseEntry, global, + telemetry, isV2: this.options.v2KnowledgeBaseEnabled, }); }; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index 3e573aff2f4c..da560dfae72d 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -15,6 +15,8 @@ import { ExecuteConnectorRequestBody, Message, Replacements } from '@kbn/elastic import { StreamResponseWithHeaders } from '@kbn/ml-response-stream/server'; import { PublicMethodsOf } from '@kbn/utility-types'; import type { InferenceServerStart } from '@kbn/inference-plugin/server'; +import { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; +import { TelemetryParams } from '@kbn/langchain/server/tracers/telemetry/telemetry_tracer'; import { ResponseBody } from '../types'; import type { AssistantTool } from '../../../types'; import { AIAssistantKnowledgeBaseDataClient } from '../../../ai_assistant_data_clients/knowledge_base'; @@ -55,6 +57,8 @@ export interface AgentExecutorParams { response?: KibanaResponseFactory; size?: number; systemPrompt?: string; + telemetry: AnalyticsServiceSetup; + telemetryParams?: TelemetryParams; traceOptions?: TraceOptions; responseLanguage?: string; } diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/helpers.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/helpers.ts index d1b3514b15b7..0126692b5b6a 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/helpers.ts @@ -7,6 +7,7 @@ import agent, { Span } from 'elastic-apm-node'; import type { Logger } from '@kbn/logging'; +import { TelemetryTracer } from '@kbn/langchain/server/tracers/telemetry'; import { streamFactory, StreamResponseWithHeaders } from '@kbn/ml-response-stream/server'; import { transformError } from '@kbn/securitysolution-es-utils'; import type { KibanaRequest } from '@kbn/core-http-server'; @@ -26,6 +27,7 @@ interface StreamGraphParams { logger: Logger; onLlmResponse?: OnLlmResponse; request: KibanaRequest; + telemetryTracer?: TelemetryTracer; traceOptions?: TraceOptions; } @@ -38,6 +40,7 @@ interface StreamGraphParams { * @param logger * @param onLlmResponse * @param request + * @param telemetryTracer * @param traceOptions */ export const streamGraph = async ({ @@ -47,6 +50,7 @@ export const streamGraph = async ({ logger, onLlmResponse, request, + telemetryTracer, traceOptions, }: StreamGraphParams): Promise => { let streamingSpan: Span | undefined; @@ -84,7 +88,11 @@ export const streamGraph = async ({ const stream = await assistantGraph.streamEvents( inputs, { - callbacks: [apmTracer, ...(traceOptions?.tracers ?? [])], + callbacks: [ + apmTracer, + ...(traceOptions?.tracers ?? []), + ...(telemetryTracer ? [telemetryTracer] : []), + ], runName: DEFAULT_ASSISTANT_GRAPH_ID, tags: traceOptions?.tags ?? [], version: 'v2', @@ -120,7 +128,11 @@ export const streamGraph = async ({ let finalMessage = ''; let conversationId: string | undefined; const stream = assistantGraph.streamEvents(inputs, { - callbacks: [apmTracer, ...(traceOptions?.tracers ?? [])], + callbacks: [ + apmTracer, + ...(traceOptions?.tracers ?? []), + ...(telemetryTracer ? [telemetryTracer] : []), + ], runName: DEFAULT_ASSISTANT_GRAPH_ID, streamMode: 'values', tags: traceOptions?.tags ?? [], @@ -187,6 +199,7 @@ interface InvokeGraphParams { assistantGraph: DefaultAssistantGraph; inputs: GraphInputs; onLlmResponse?: OnLlmResponse; + telemetryTracer?: TelemetryTracer; traceOptions?: TraceOptions; } interface InvokeGraphResponse { @@ -202,6 +215,7 @@ interface InvokeGraphResponse { * @param assistantGraph * @param inputs * @param onLlmResponse + * @param telemetryTracer * @param traceOptions */ export const invokeGraph = async ({ @@ -209,6 +223,7 @@ export const invokeGraph = async ({ assistantGraph, inputs, onLlmResponse, + telemetryTracer, traceOptions, }: InvokeGraphParams): Promise => { return withAssistantSpan(DEFAULT_ASSISTANT_GRAPH_ID, async (span) => { @@ -222,7 +237,11 @@ export const invokeGraph = async ({ span.addLabels({ evaluationId: traceOptions?.evaluationId }); } const r = await assistantGraph.invoke(inputs, { - callbacks: [apmTracer, ...(traceOptions?.tracers ?? [])], + callbacks: [ + apmTracer, + ...(traceOptions?.tracers ?? []), + ...(telemetryTracer ? [telemetryTracer] : []), + ], runName: DEFAULT_ASSISTANT_GRAPH_ID, tags: traceOptions?.tags ?? [], }); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts index 4f043c681f8d..f55006e452cd 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts @@ -13,6 +13,7 @@ import { createToolCallingAgent, } from 'langchain/agents'; import { APMTracer } from '@kbn/langchain/server/tracers/apm'; +import { TelemetryTracer } from '@kbn/langchain/server/tracers/telemetry'; import { getLlmClass } from '../../../../routes/utils'; import { EsAnonymizationFieldsSchema } from '../../../../ai_assistant_data_clients/anonymization_fields/types'; import { AssistantToolParams } from '../../../../types'; @@ -44,6 +45,8 @@ export const callAssistantGraph: AgentExecutor = async ({ request, size, systemPrompt, + telemetry, + telemetryParams, traceOptions, responseLanguage = 'English', }) => { @@ -107,6 +110,7 @@ export const callAssistantGraph: AgentExecutor = async ({ replacements, request, size, + telemetry, }; const tools: StructuredTool[] = assistantTools.flatMap( @@ -150,7 +154,17 @@ export const callAssistantGraph: AgentExecutor = async ({ }); const apmTracer = new APMTracer({ projectName: traceOptions?.projectName ?? 'default' }, logger); - + const telemetryTracer = telemetryParams + ? new TelemetryTracer( + { + elasticTools: assistantTools.map(({ name }) => name), + totalTools: tools.length, + telemetry, + telemetryParams, + }, + logger + ) + : undefined; const assistantGraph = getDefaultAssistantGraph({ agentRunnable, dataClients, @@ -177,6 +191,7 @@ export const callAssistantGraph: AgentExecutor = async ({ logger, onLlmResponse, request, + telemetryTracer, traceOptions, }); } @@ -186,6 +201,7 @@ export const callAssistantGraph: AgentExecutor = async ({ assistantGraph, inputs, onLlmResponse, + telemetryTracer, traceOptions, }); diff --git a/x-pack/plugins/elastic_assistant/server/lib/telemetry/event_based_telemetry.ts b/x-pack/plugins/elastic_assistant/server/lib/telemetry/event_based_telemetry.ts index 5ff5ff894dff..1087703ba13a 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/telemetry/event_based_telemetry.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/telemetry/event_based_telemetry.ts @@ -76,7 +76,16 @@ export const INVOKE_ASSISTANT_SUCCESS_EVENT: EventTypeOpts<{ assistantStreamingEnabled: boolean; actionTypeId: string; isEnabledKnowledgeBase: boolean; + durationMs: number; + ['toolsInvoked.AlertCountsTool']?: number; + ['toolsInvoked.NaturalLanguageESQLTool']?: number; + ['toolsInvoked.KnowledgeBaseRetrievalTool']?: number; + ['toolsInvoked.KnowledgeBaseWriteTool']?: number; + ['toolsInvoked.OpenAndAcknowledgedAlertsTool']?: number; + ['toolsInvoked.SecurityLabsKnowledgeBaseTool']?: number; + ['toolsInvoked.CustomTool']?: number; model?: string; + isOssModel?: boolean; }> = { eventType: 'invoke_assistant_success', schema: { @@ -105,6 +114,68 @@ export const INVOKE_ASSISTANT_SUCCESS_EVENT: EventTypeOpts<{ description: 'Is knowledge base enabled', }, }, + isOssModel: { + type: 'boolean', + _meta: { + description: 'Is OSS model used on the request', + optional: true, + }, + }, + durationMs: { + type: 'integer', + _meta: { + description: 'The duration of the request.', + }, + }, + 'toolsInvoked.AlertCountsTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.NaturalLanguageESQLTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.KnowledgeBaseRetrievalTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.KnowledgeBaseWriteTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.OpenAndAcknowledgedAlertsTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.SecurityLabsKnowledgeBaseTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, + 'toolsInvoked.CustomTool': { + type: 'long', + _meta: { + description: 'Number of times tool was invoked.', + optional: true, + }, + }, }, }; @@ -261,9 +332,90 @@ export const ATTACK_DISCOVERY_ERROR_EVENT: EventTypeOpts<{ }, }; +export const CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT: EventTypeOpts<{ + entryType: 'index' | 'document'; + required: boolean; + sharing: 'private' | 'global'; + source?: string; +}> = { + eventType: 'create_knowledge_base_entry_success', + schema: { + entryType: { + type: 'keyword', + _meta: { + description: 'Index entry or document entry', + }, + }, + sharing: { + type: 'keyword', + _meta: { + description: 'Sharing setting: private or global', + }, + }, + required: { + type: 'boolean', + _meta: { + description: 'Whether this resource should always be included', + }, + }, + source: { + type: 'keyword', + _meta: { + description: 'Where the knowledge base document entry was created', + optional: true, + }, + }, + }, +}; + +export const CREATE_KNOWLEDGE_BASE_ENTRY_ERROR_EVENT: EventTypeOpts<{ + entryType: 'index' | 'document'; + required: boolean; + sharing: 'private' | 'global'; + source?: string; + errorMessage: string; +}> = { + eventType: 'create_knowledge_base_entry_error', + schema: { + entryType: { + type: 'keyword', + _meta: { + description: 'Index entry or document entry', + }, + }, + sharing: { + type: 'keyword', + _meta: { + description: 'Sharing setting: private or global', + }, + }, + required: { + type: 'boolean', + _meta: { + description: 'Whether this resource should always be included', + }, + }, + source: { + type: 'keyword', + _meta: { + description: 'Where the knowledge base document entry was created', + optional: true, + }, + }, + errorMessage: { + type: 'keyword', + _meta: { + description: 'Error message', + }, + }, + }, +}; + export const events: Array> = [ KNOWLEDGE_BASE_EXECUTION_SUCCESS_EVENT, KNOWLEDGE_BASE_EXECUTION_ERROR_EVENT, + CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT, + CREATE_KNOWLEDGE_BASE_ENTRY_ERROR_EVENT, INVOKE_ASSISTANT_SUCCESS_EVENT, INVOKE_ASSISTANT_ERROR_EVENT, ATTACK_DISCOVERY_SUCCESS_EVENT, diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts index d5db24d44f3e..e4f520b190b5 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts @@ -282,6 +282,7 @@ export const postEvaluateRoute = ( inference, connectorId: connector.id, size, + telemetry: ctx.elasticAssistant.telemetry, }; const tools: StructuredTool[] = assistantTools.flatMap( diff --git a/x-pack/plugins/elastic_assistant/server/routes/helpers.ts b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts index d25ed5fc77f1..0c5c39f77d69 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts @@ -30,6 +30,7 @@ import { ActionsClient } from '@kbn/actions-plugin/server'; import { AssistantFeatureKey } from '@kbn/elastic-assistant-common/impl/capabilities'; import { getLangSmithTracer } from '@kbn/langchain/server/tracers/langsmith'; import type { InferenceServerStart } from '@kbn/inference-plugin/server'; +import { INVOKE_ASSISTANT_SUCCESS_EVENT } from '../lib/telemetry/event_based_telemetry'; import { AIAssistantKnowledgeBaseDataClient } from '../ai_assistant_data_clients/knowledge_base'; import { FindResponse } from '../ai_assistant_data_clients/find'; import { EsPromptsSchema } from '../ai_assistant_data_clients/prompts/types'; @@ -46,7 +47,6 @@ import { executeAction, StaticResponse } from '../lib/executor'; import { getLangChainMessages } from '../lib/langchain/helpers'; import { AIAssistantConversationsDataClient } from '../ai_assistant_data_clients/conversations'; -import { INVOKE_ASSISTANT_SUCCESS_EVENT } from '../lib/telemetry/event_based_telemetry'; import { ElasticAssistantRequestHandlerContext, GetElser } from '../types'; import { callAssistantGraph } from '../lib/langchain/graphs/default_assistant_graph'; @@ -399,6 +399,7 @@ export const langChainExecute = async ({ kbDataClient, }; + const isKnowledgeBaseInstalled = await getIsKnowledgeBaseInstalled(kbDataClient); // Shared executor params const executorParams: AgentExecutorParams = { abortSignal, @@ -422,6 +423,14 @@ export const langChainExecute = async ({ responseLanguage, size: request.body.size, systemPrompt, + telemetry, + telemetryParams: { + actionTypeId, + model: request.body.model, + assistantStreamingEnabled: isStream, + isEnabledKnowledgeBase: isKnowledgeBaseInstalled, + eventType: INVOKE_ASSISTANT_SUCCESS_EVENT.eventType, + }, traceOptions: { projectName: request.body.langSmithProject, tracers: getLangSmithTracer({ @@ -436,14 +445,6 @@ export const langChainExecute = async ({ executorParams ); - const isKnowledgeBaseInstalled = await getIsKnowledgeBaseInstalled(kbDataClient); - - telemetry.reportEvent(INVOKE_ASSISTANT_SUCCESS_EVENT.eventType, { - actionTypeId, - model: request.body.model, - assistantStreamingEnabled: isStream, - isEnabledKnowledgeBase: isKnowledgeBaseInstalled, - }); return response.ok(result); }; diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts index fbe73525578b..fc49068a09cc 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import type { IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server'; +import { AnalyticsServiceSetup, IKibanaResponse, KibanaResponseFactory } from '@kbn/core/server'; import { transformError } from '@kbn/securitysolution-es-utils'; import { @@ -20,6 +20,7 @@ import { } from '@kbn/elastic-assistant-common'; import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; +import { CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT } from '../../../lib/telemetry/event_based_telemetry'; import { performChecks } from '../../helpers'; import { KNOWLEDGE_BASE_ENTRIES_TABLE_MAX_PAGE_SIZE } from '../../../../common/constants'; import { @@ -62,7 +63,8 @@ const buildBulkResponse = ( created = [], deleted = [], skipped = [], - }: KnowledgeBaseEntryBulkCrudActionResults & { errors: BulkOperationError[] } + }: KnowledgeBaseEntryBulkCrudActionResults & { errors: BulkOperationError[] }, + telemetry: AnalyticsServiceSetup ): IKibanaResponse => { const numSucceeded = updated.length + created.length + deleted.length; const numSkipped = skipped.length; @@ -82,6 +84,16 @@ const buildBulkResponse = ( skipped, }; + if (created.length) { + created.forEach((entry) => { + telemetry.reportEvent(CREATE_KNOWLEDGE_BASE_ENTRY_SUCCESS_EVENT.eventType, { + entryType: entry.type, + required: 'required' in entry ? entry.required ?? false : false, + sharing: entry.users.length ? 'private' : 'global', + ...(entry.type === 'document' ? { source: entry.source } : {}), + }); + }); + } if (numFailed > 0) { return response.custom({ headers: { 'content-type': 'application/json' }, @@ -289,14 +301,18 @@ export const bulkActionKnowledgeBaseEntriesRoute = (router: ElasticAssistantPlug }) : undefined; - return buildBulkResponse(response, { - // @ts-ignore-next-line TS2322 - updated: transformESToKnowledgeBase(docsUpdated), - created: created?.data ? transformESSearchToKnowledgeBaseEntry(created?.data) : [], - deleted: docsDeleted ?? [], - skipped: [], - errors, - }); + return buildBulkResponse( + response, + { + // @ts-ignore-next-line TS2322 + updated: transformESToKnowledgeBase(docsUpdated), + created: created?.data ? transformESSearchToKnowledgeBaseEntry(created?.data) : [], + deleted: docsDeleted ?? [], + skipped: [], + errors, + }, + ctx.elasticAssistant.telemetry + ); } catch (err) { const error = transformError(err); return assistantResponse.error({ diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts index 0bfe9de269f7..d5df2d02055f 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/create_route.ts @@ -65,6 +65,7 @@ export const createKnowledgeBaseEntryRoute = (router: ElasticAssistantPluginRout const createResponse = await kbDataClient?.createKnowledgeBaseEntry({ knowledgeBaseEntry: request.body, global: request.body.users != null && request.body.users.length === 0, + telemetry: ctx.elasticAssistant.telemetry, }); if (createResponse == null) { diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts index e84b97ab43d7..00fec0dcabc6 100755 --- a/x-pack/plugins/elastic_assistant/server/types.ts +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -252,4 +252,5 @@ export interface AssistantToolParams { ExecuteConnectorRequestBody | AttackDiscoveryPostRequestBody >; size?: number; + telemetry?: AnalyticsServiceSetup; } diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json index d3436f28a1d3..52ed30dde67f 100644 --- a/x-pack/plugins/elastic_assistant/tsconfig.json +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -49,7 +49,8 @@ "@kbn/std", "@kbn/zod", "@kbn/inference-plugin", - "@kbn/data-views-plugin" + "@kbn/data-views-plugin", + "@kbn/core-analytics-server" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts index 4069eeeef5b9..3ae47afbf05b 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts @@ -12,10 +12,12 @@ import type { AIAssistantKnowledgeBaseDataClient } from '@kbn/elastic-assistant- import { DocumentEntryType } from '@kbn/elastic-assistant-common'; import type { KnowledgeBaseEntryCreateProps } from '@kbn/elastic-assistant-common'; import type { LegacyKnowledgeBaseEntryCreateProps } from '@kbn/elastic-assistant-plugin/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry'; +import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import { APP_UI_ID } from '../../../../common'; export interface KnowledgeBaseWriteToolParams extends AssistantToolParams { kbDataClient: AIAssistantKnowledgeBaseDataClient; + telemetry: AnalyticsServiceSetup; } const toolDetails = { @@ -34,7 +36,7 @@ export const KNOWLEDGE_BASE_WRITE_TOOL: AssistantTool = { getTool(params: AssistantToolParams) { if (!this.isSupported(params)) return null; - const { kbDataClient, logger } = params as KnowledgeBaseWriteToolParams; + const { telemetry, kbDataClient, logger } = params as KnowledgeBaseWriteToolParams; if (kbDataClient == null) return null; return new DynamicStructuredTool({ @@ -77,7 +79,7 @@ export const KNOWLEDGE_BASE_WRITE_TOOL: AssistantTool = { }; logger.debug(() => `knowledgeBaseEntry\n ${JSON.stringify(knowledgeBaseEntry, null, 2)}`); - const resp = await kbDataClient.createKnowledgeBaseEntry({ knowledgeBaseEntry }); + const resp = await kbDataClient.createKnowledgeBaseEntry({ knowledgeBaseEntry, telemetry }); if (resp == null) { return "I'm sorry, but I was unable to add this entry to your knowledge base."; From 4e65ae9b1e69fc92341de99078bde179708b3394 Mon Sep 17 00:00:00 2001 From: Cee Chen <549407+cee-chen@users.noreply.github.com> Date: Mon, 11 Nov 2024 17:02:34 -0800 Subject: [PATCH 034/100] Upgrade EUI to v97.3.1 (#199186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `v97.3.0`⏩`v97.3.1` _[Questions? Please see our Kibana upgrade FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)_ --- ## [`v97.3.1`](https://github.com/elastic/eui/releases/v97.3.1) **Bug fixes** - Fixed an `EuiComboBox` bug where Enter keypresses were not working correctly on selection clear buttons ([#8105](https://github.com/elastic/eui/pull/8105)) - Fixed an `EuiSuperDatePicker` bug where inputs would overflow out of smaller widths instead of truncating ([#8109](https://github.com/elastic/eui/pull/8109)) - Fixed a bug with `EuiPageHeader`'s `rightSideItems` responsiveness where single items could overflow past the intended max width ([#8110](https://github.com/elastic/eui/pull/8110)) --- package.json | 2 +- .../list_header/__snapshots__/list_header.test.tsx.snap | 8 ++++---- packages/kbn-test-jest-helpers/src/testbed/testbed.ts | 2 +- .../impl/src/__snapshots__/page_template.test.tsx.snap | 2 +- src/dev/license_checker/config.ts | 2 +- .../components/__snapshots__/header.test.tsx.snap | 4 ++-- .../public/application/components/color_rules.test.tsx | 2 +- yarn.lock | 8 ++++---- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 0ad1d96d7b5d..ef0d751ff752 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "^8.15.1", "@elastic/ems-client": "8.5.3", - "@elastic/eui": "97.3.0", + "@elastic/eui": "97.3.1", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "^1.2.3", "@elastic/numeral": "^2.5.1", diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index 5f861bace955..ff618a160e61 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -149,7 +149,7 @@ exports[`ExceptionListHeader should render edit modal 1`] = ` class="euiFlexGroup emotion-euiFlexGroup-wrap-l-flexStart-stretch-row-euiPageHeaderContent__rightSideItems" >
test
diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 8609eef92a26..91c3903a279d 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -87,7 +87,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.3': ['Elastic License 2.0'], - '@elastic/eui@97.3.0': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], + '@elastic/eui@97.3.1': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap index ef2699747a6b..81e3d2122ebf 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/header.test.tsx.snap @@ -23,7 +23,7 @@ exports[`Intro component renders correctly 1`] = ` class="euiFlexGroup emotion-euiFlexGroup-wrap-l-flexStart-stretch-row-euiPageHeaderContent__rightSideItems" >
{ collectionActions.handleChange = jest.fn(); const wrapper = mountWithIntl(); - const operatorInput = findTestSubject(wrapper, 'colorRuleOperator'); + const operatorInput = findTestSubject(wrapper, 'colorRuleOperator').find('input'); operatorInput.simulate('keyDown', { key: keys.ARROW_DOWN }); operatorInput.simulate('keyDown', { key: keys.ARROW_DOWN }); operatorInput.simulate('keyDown', { key: keys.ENTER }); diff --git a/yarn.lock b/yarn.lock index 47ee0df93099..e5e3c6b0020b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1748,10 +1748,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@97.3.0": - version "97.3.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-97.3.0.tgz#3961e39a6a8ac38e1af999baf0e96de8e1671943" - integrity sha512-Ic9DXHlh9yVumYypoLSM+plM0xBjSPc8PPRT4z5bHXLXZrLuSEVoqfix3co5yl4+ibLwfxNPCZFflbFiMl2apA== +"@elastic/eui@97.3.1": + version "97.3.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-97.3.1.tgz#b0f07c603042bd359544b41829507e65f4fa3cd2" + integrity sha512-zJs3aaO6qjTdxJM2mPahcqaC6FfaC34yTc3qpQq7+Cbhw2xGrwx8bAfIzhttLU87mwgr59Sqv9Ojvwk8c3js7A== dependencies: "@hello-pangea/dnd" "^16.6.0" "@types/lodash" "^4.14.202" From abf6a1d4f6e83f108ed11a3e6279d73db514b352 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Mon, 11 Nov 2024 17:50:29 -0800 Subject: [PATCH 035/100] [Inference Connector] Modified getProvider to use _inference/_services ES API instead of hardcoded values. (#199047) @ymao1 [introduced](https://github.com/elastic/elasticsearch/pull/114862) new ES API which allows to fetch available services providers list with the settings and task types: `GET _inference/_services` This PR is changing hardcoded providers list `x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts` to use new ES API. --- .../lib => common}/dynamic_config/types.ts | 0 .../common/inference/types.ts | 15 + .../inference/additional_options_fields.tsx | 4 +- .../inference/connector.test.tsx | 75 +- .../connector_types/inference/connector.tsx | 59 +- .../inference/get_task_types.test.ts | 50 - .../inference/get_task_types.ts | 606 ---------- .../connector_types/inference/helpers.ts | 2 +- .../inference/hidden_fields.tsx | 2 +- .../inference/providers/get_providers.ts | 1022 +---------------- .../service_provider.tsx | 6 +- .../inference/providers/selectable/index.tsx | 23 +- .../public/connector_types/inference/types.ts | 3 - .../connector_configuration_field.tsx | 2 +- .../connector_configuration_form_items.tsx | 2 +- .../connector_configuration_utils.ts | 2 +- .../plugins/stack_connectors/server/plugin.ts | 7 +- .../routes/get_inference_services.test.ts | 133 +++ .../server/routes/get_inference_services.ts | 48 + .../stack_connectors/server/routes/index.ts | 1 + 20 files changed, 301 insertions(+), 1761 deletions(-) rename x-pack/plugins/stack_connectors/{public/connector_types/lib => common}/dynamic_config/types.ts (100%) delete mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts delete mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts create mode 100644 x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts create mode 100644 x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts b/x-pack/plugins/stack_connectors/common/dynamic_config/types.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts rename to x-pack/plugins/stack_connectors/common/dynamic_config/types.ts diff --git a/x-pack/plugins/stack_connectors/common/inference/types.ts b/x-pack/plugins/stack_connectors/common/inference/types.ts index 9dbd447cb457..b9561efe2429 100644 --- a/x-pack/plugins/stack_connectors/common/inference/types.ts +++ b/x-pack/plugins/stack_connectors/common/inference/types.ts @@ -19,6 +19,7 @@ import { TextEmbeddingParamsSchema, TextEmbeddingResponseSchema, } from './schema'; +import { ConfigProperties } from '../dynamic_config/types'; export type Config = TypeOf; export type Secrets = TypeOf; @@ -36,3 +37,17 @@ export type TextEmbeddingParams = TypeOf; export type TextEmbeddingResponse = TypeOf; export type StreamingResponse = TypeOf; + +export type FieldsConfiguration = Record; + +export interface InferenceTaskType { + task_type: string; + configuration: FieldsConfiguration; +} + +export interface InferenceProvider { + provider: string; + task_types: InferenceTaskType[]; + logo?: string; + configuration: FieldsConfiguration; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx index 8973f3124bc8..7a3b1abfd800 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx @@ -32,10 +32,10 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; import * as i18n from './translations'; import { DEFAULT_TASK_TYPE } from './constants'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; import { Config } from './types'; import { TaskTypeOption } from './helpers'; @@ -52,7 +52,7 @@ interface AdditionalOptionsConnectorFieldsProps { isEdit: boolean; optionalProviderFormFields: ConfigEntryView[]; onSetProviderConfigEntry: (key: string, value: unknown) => Promise; - onTaskTypeOptionsSelect: (taskType: string, provider?: string) => Promise; + onTaskTypeOptionsSelect: (taskType: string, provider?: string) => void; selectedTaskType?: string; taskTypeFormFields: ConfigEntryView[]; taskTypeSchema: ConfigEntryView[]; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx index 44632e8b0833..d445504011b5 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx @@ -12,13 +12,10 @@ import { ConnectorFormTestProvider } from '../lib/test_utils'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock'; -import { DisplayType, FieldType } from '../lib/dynamic_config/types'; import { useProviders } from './providers/get_providers'; -import { getTaskTypes } from './get_task_types'; -import { HttpSetup } from '@kbn/core-http-browser'; +import { DisplayType, FieldType } from '../../../common/dynamic_config/types'; jest.mock('./providers/get_providers'); -jest.mock('./get_task_types'); const mockUseKibanaReturnValue = createStartServicesMock(); jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana', () => ({ @@ -37,13 +34,32 @@ jest.mock('@faker-js/faker', () => ({ })); const mockProviders = useProviders as jest.Mock; -const mockTaskTypes = getTaskTypes as jest.Mock; const providersSchemas = [ { provider: 'openai', logo: '', // should be openai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], + task_types: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], configuration: { api_key: { display: DisplayType.TEXTBOX, @@ -106,7 +122,16 @@ const providersSchemas = [ { provider: 'googleaistudio', logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], + task_types: [ + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], configuration: { api_key: { display: DisplayType.TEXTBOX, @@ -139,39 +164,6 @@ const providersSchemas = [ }, }, ]; -const taskTypesSchemas: Record = { - googleaistudio: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - openai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], -}; const openAiConnector = { actionTypeId: '.inference', @@ -222,9 +214,6 @@ describe('ConnectorFields renders', () => { isLoading: false, data: providersSchemas, }); - mockTaskTypes.mockImplementation( - (http: HttpSetup, provider: string) => taskTypesSchemas[provider] - ); }); test('openai provider fields are rendered', async () => { const { getAllByTestId } = render( diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx index 35314dc06167..5f854384dbb5 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { EuiFormRow, EuiSpacer, @@ -31,12 +31,12 @@ import { import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; +import { InferenceTaskType } from '../../../common/inference/types'; import { ServiceProviderKeys } from '../../../common/inference/constants'; import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; -import { getTaskTypes } from './get_task_types'; import * as i18n from './translations'; import { DEFAULT_TASK_TYPE } from './constants'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; import { SelectableProvider } from './providers/selectable'; import { Config, Secrets } from './types'; import { generateInferenceEndpointId, getTaskTypeOptions, TaskTypeOption } from './helpers'; @@ -116,13 +116,13 @@ const InferenceAPIConnectorFields: React.FunctionComponent { + (taskType: string, provider?: string) => { // Get task type settings - const currentTaskTypes = await getTaskTypes(http, provider ?? config?.provider); + const currentProvider = providers?.find((p) => p.provider === (provider ?? config?.provider)); + const currentTaskTypes = currentProvider?.task_types; const newTaskType = currentTaskTypes?.find((p) => p.task_type === taskType); setSelectedTaskType(taskType); - generateInferenceEndpointId(config, setFieldValue); // transform the schema const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ @@ -150,19 +150,23 @@ const InferenceAPIConnectorFields: React.FunctionComponent { + (provider?: string) => { const newProvider = providers?.find((p) => p.provider === provider); // Update task types list available for the selected provider - const providerTaskTypes = newProvider?.taskTypes ?? []; + const providerTaskTypes = (newProvider?.task_types ?? []).map((t) => t.task_type); setTaskTypeOptions(getTaskTypeOptions(providerTaskTypes)); if (providerTaskTypes.length > 0) { - await onTaskTypeOptionsSelect(providerTaskTypes[0], provider); + onTaskTypeOptionsSelect(providerTaskTypes[0], provider); } // Update connector providerSchema @@ -203,9 +207,8 @@ const InferenceAPIConnectorFields: React.FunctionComponent { - const getTaskTypeSchema = async () => { - const currentTaskTypes = await getTaskTypes(http, config?.provider ?? ''); - const newTaskType = currentTaskTypes?.find((p) => p.task_type === config?.taskType); + const getTaskTypeSchema = (taskTypes: InferenceTaskType[]) => { + const newTaskType = taskTypes.find((p) => p.task_type === config?.taskType); // transform the schema const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ @@ -228,7 +231,7 @@ const InferenceAPIConnectorFields: React.FunctionComponent + Object.keys(SERVICE_PROVIDERS).includes(config?.provider) + ? SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].icon + : undefined, + [config?.provider] + ); + + const providerName = useMemo( + () => + Object.keys(SERVICE_PROVIDERS).includes(config?.provider) + ? SERVICE_PROVIDERS[config?.provider as ServiceProviderKeys].name + : config?.provider, + [config?.provider] + ); + const providerSuperSelect = useCallback( (isInvalid: boolean) => ( jest.resetAllMocks()); - -describe.skip('getTaskTypes', () => { - test('should call get inference task types api', async () => { - const apiResponse = { - amazonbedrock: [ - { - task_type: 'completion', - configuration: { - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Sets the maximum number for the output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - }; - http.get.mockResolvedValueOnce(apiResponse); - - const result = await getTaskTypes(http, 'amazonbedrock'); - expect(result).toEqual(apiResponse); - }); -}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts deleted file mode 100644 index a4fbbd6a6288..000000000000 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { HttpSetup } from '@kbn/core-http-browser'; -import { DisplayType, FieldType } from '../lib/dynamic_config/types'; -import { FieldsConfiguration } from './types'; - -export interface InferenceTaskType { - task_type: string; - configuration: FieldsConfiguration; -} - -// this http param is for the future migrating to real API -export const getTaskTypes = (http: HttpSetup, provider: string): Promise => { - const providersTaskTypes: Record = { - openai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - mistral: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - hugging_face: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - googlevertexai: [ - { - task_type: 'text_embedding', - configuration: { - auto_truncate: { - display: DisplayType.TOGGLE, - label: 'Auto truncate', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Specifies if the API truncates inputs longer than the maximum token length automatically.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'rerank', - configuration: { - top_n: { - display: DisplayType.TOGGLE, - label: 'Top N', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the number of the top n documents, which should be returned.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - googleaistudio: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - elasticsearch: [ - { - task_type: 'rerank', - configuration: { - return_documents: { - display: DisplayType.TOGGLE, - label: 'Return documents', - options: [], - order: 1, - required: false, - sensitive: false, - tooltip: 'Returns the document instead of only the index.', - type: FieldType.BOOLEAN, - validations: [], - value: true, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'sparse_embedding', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - cohere: [ - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'text_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'classification', - value: 'classification', - }, - { - label: 'clusterning', - value: 'clusterning', - }, - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - truncate: { - display: DisplayType.DROPDOWN, - options: [ - { - label: 'NONE', - value: 'NONE', - }, - { - label: 'START', - value: 'START', - }, - { - label: 'END', - value: 'END', - }, - ], - label: 'Truncate', - order: 2, - required: false, - sensitive: false, - tooltip: 'Specifies how the API handles inputs longer than the maximum token length.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'rerank', - configuration: { - return_documents: { - display: DisplayType.TOGGLE, - label: 'Return documents', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specify whether to return doc text within the results.', - type: FieldType.BOOLEAN, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_n: { - display: DisplayType.NUMERIC, - label: 'Top N', - order: 1, - required: false, - sensitive: false, - tooltip: - 'The number of most relevant documents to return, defaults to the number of the documents.', - type: FieldType.INTEGER, - validations: [], - value: false, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - azureopenai: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - azureaistudio: [ - { - task_type: 'completion', - configuration: { - user: { - display: DisplayType.TEXTBOX, - label: 'User', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the user issuing the request.', - type: FieldType.STRING, - validations: [], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: { - do_sample: { - display: DisplayType.NUMERIC, - label: 'Do sample', - order: 1, - required: false, - sensitive: false, - tooltip: 'Instructs the inference process to perform sampling or not.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Provides a hint for the maximum number of output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.NUMERIC, - label: 'Temperature', - order: 1, - required: false, - sensitive: false, - tooltip: 'A number in the range of 0.0 to 2.0 that specifies the sampling temperature.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 1, - required: false, - sensitive: false, - tooltip: - 'A number in the range of 0.0 to 2.0 that is an alternative value to temperature. Should not be used if temperature is specified.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - amazonbedrock: [ - { - task_type: 'completion', - configuration: { - max_new_tokens: { - display: DisplayType.NUMERIC, - label: 'Max new tokens', - order: 1, - required: false, - sensitive: false, - tooltip: 'Sets the maximum number for the output tokens to be generated.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.NUMERIC, - label: 'Temperature', - order: 1, - required: false, - sensitive: false, - tooltip: - 'A number between 0.0 and 1.0 that controls the apparent creativity of the results.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Alternative to temperature. A number in the range of 0.0 to 1.0, to eliminate low-probability tokens.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_k: { - display: DisplayType.NUMERIC, - label: 'Top K', - order: 1, - required: false, - sensitive: false, - tooltip: - 'Only available for anthropic, cohere, and mistral providers. Alternative to temperature.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - anthropic: [ - { - task_type: 'completion', - configuration: { - max_tokens: { - display: DisplayType.NUMERIC, - label: 'Max tokens', - order: 1, - required: true, - sensitive: false, - tooltip: 'The maximum number of tokens to generate before stopping.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - temperature: { - display: DisplayType.TEXTBOX, - label: 'Temperature', - order: 2, - required: false, - sensitive: false, - tooltip: 'The amount of randomness injected into the response.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_p: { - display: DisplayType.NUMERIC, - label: 'Top P', - order: 4, - required: false, - sensitive: false, - tooltip: 'Specifies to use Anthropic’s nucleus sampling.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - top_k: { - display: DisplayType.NUMERIC, - label: 'Top K', - order: 3, - required: false, - sensitive: false, - tooltip: 'Specifies to only sample from the top K options for each subsequent token.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ], - 'alibabacloud-ai-search': [ - { - task_type: 'text_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'sparse_embedding', - configuration: { - input_type: { - display: DisplayType.DROPDOWN, - label: 'Input type', - order: 1, - required: false, - sensitive: false, - tooltip: 'Specifies the type of input passed to the model.', - type: FieldType.STRING, - validations: [], - options: [ - { - label: 'ingest', - value: 'ingest', - }, - { - label: 'search', - value: 'search', - }, - ], - value: '', - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - return_token: { - display: DisplayType.TOGGLE, - label: 'Return token', - options: [], - order: 1, - required: false, - sensitive: false, - tooltip: - 'If `true`, the token name will be returned in the response. Defaults to `false` which means only the token ID will be returned in the response.', - type: FieldType.BOOLEAN, - validations: [], - value: true, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - task_type: 'completion', - configuration: {}, - }, - { - task_type: 'rerank', - configuration: {}, - }, - ], - watsonxai: [ - { - task_type: 'text_embedding', - configuration: {}, - }, - ], - }; - return Promise.resolve(providersTaskTypes[provider]); -}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts index 0e1e4cdaa41a..8638caa998ef 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts @@ -7,7 +7,7 @@ import { isEmpty } from 'lodash/fp'; import { ValidationFunc } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { Config } from './types'; import * as i18n from './translations'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx index 9b28d35aaaf3..f6df891b4b9c 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { HiddenField } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { ConfigEntryView } from '../../../common/dynamic_config/types'; import { getNonEmptyValidator } from './helpers'; -import { ConfigEntryView } from '../lib/dynamic_config/types'; export const getProviderSecretsHiddenField = ( providerSchema: ConfigEntryView[], diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts index 109266c1273f..badc0cb61030 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts @@ -9,1025 +9,11 @@ import type { HttpSetup } from '@kbn/core-http-browser'; import { i18n } from '@kbn/i18n'; import { useQuery } from '@tanstack/react-query'; import type { ToastsStart } from '@kbn/core-notifications-browser'; -import { DisplayType, FieldType } from '../../lib/dynamic_config/types'; -import { FieldsConfiguration } from '../types'; +import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../../../common'; +import { InferenceProvider } from '../../../../common/inference/types'; -export interface InferenceProvider { - provider: string; - taskTypes: string[]; - logo?: string; - configuration: FieldsConfiguration; -} - -export const getProviders = (http: HttpSetup): Promise => { - const providers = [ - { - provider: 'openai', - logo: '', // should be openai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 3, - required: true, - sensitive: true, - tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: 'The name of the model to use for the inference task.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - organization_id: { - display: DisplayType.TEXTBOX, - label: 'Organization ID', - order: 4, - required: false, - sensitive: false, - tooltip: 'The unique identifier of your organization.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 1, - required: true, - sensitive: false, - tooltip: - 'The OpenAI API endpoint URL. For more information on the URL, refer to the https://platform.openai.com/docs/api-reference.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: 'https://api.openai.com/v1/chat/completions', - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'Default number of requests allowed per minute. For text_embedding is 3000. For completion is 500.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'googleaistudio', - logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `ID of the LLM you're using`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'amazonbedrock', - logo: '', // should be amazonbedrock logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - access_key: { - display: DisplayType.TEXTBOX, - label: 'Access Key', - order: 1, - required: true, - sensitive: true, - tooltip: `A valid AWS access key that has permissions to use Amazon Bedrock.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - secret_key: { - display: DisplayType.TEXTBOX, - label: 'Secret Key', - order: 2, - required: true, - sensitive: true, - tooltip: `A valid AWS secret key that is paired with the access_key.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - provider: { - display: DisplayType.DROPDOWN, - label: 'Provider', - order: 3, - required: true, - options: [ - { - label: 'amazontitan', - value: 'amazontitan', - }, - { - label: 'anthropic', - value: 'anthropic', - }, - { - label: 'ai21labs', - value: 'ai21labs', - }, - { - label: 'cohere', - value: 'cohere', - }, - { - label: 'meta', - value: 'meta', - }, - { - label: 'mistral', - value: 'mistral', - }, - ], - sensitive: false, - tooltip: 'The model provider for your deployment.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model: { - display: DisplayType.TEXTBOX, - label: 'Model', - order: 4, - required: true, - sensitive: false, - tooltip: `The base model ID or an ARN to a custom model based on a foundational model.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - region: { - display: DisplayType.TEXTBOX, - label: 'Region', - order: 5, - required: true, - sensitive: false, - tooltip: `The region that your model or ARN is deployed in.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 6, - required: false, - sensitive: false, - tooltip: - 'By default, the amazonbedrock service sets the number of requests allowed per minute to 240.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'googlevertexai', - logo: '', // should be googlevertexai logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding', 'rerank'], - configuration: { - service_account_json: { - display: DisplayType.TEXTBOX, - label: 'Credentials JSON', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `ID of the LLM you're using`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - location: { - display: DisplayType.TEXTBOX, - label: 'GCP Region', - order: 2, - required: true, - sensitive: false, - tooltip: `Please provide the GCP region where the Vertex AI API(s) is enabled. For more information, refer to the {geminiVertexAIDocs}.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - project_id: { - display: DisplayType.TEXTBOX, - label: 'GCP Project', - order: 3, - required: true, - sensitive: false, - tooltip: - 'The GCP Project ID which has Vertex AI API(s) enabled. For more information on the URL, refer to the {geminiVertexAIDocs}.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'mistral', - logo: '', // should be misral logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model: { - display: DisplayType.TEXTBOX, - label: 'Model', - order: 2, - required: true, - sensitive: false, - tooltip: `Refer to the Mistral models documentation for the list of available text embedding models`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 4, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 240, - depends_on: [], - }, - max_input_tokens: { - display: DisplayType.NUMERIC, - label: 'Maximum input tokens', - order: 3, - required: false, - sensitive: false, - tooltip: 'Allows you to specify the maximum number of tokens per input.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'hugging_face', - logo: '', // should be hugging_face logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 2, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 1, - required: true, - sensitive: false, - tooltip: 'The URL endpoint to use for the requests.', - type: FieldType.STRING, - validations: [], - value: 'https://api.openai.com/v1/embeddings', - ui_restrictions: [], - default_value: 'https://api.openai.com/v1/embeddings', - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 3, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'elasticsearch', - logo: '', // elasticsearch logo here - taskTypes: ['sparse_embedding', 'text_embedding', 'rerank'], - configuration: { - model_id: { - display: DisplayType.DROPDOWN, - label: 'Model ID', - order: 1, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - options: [ - { - label: '.elser_model_1', - value: '.elser_model_1', - }, - { - label: '.elser_model_2', - value: '.elser_model_2', - }, - { - label: '.elser_model_2_linux-x86_64', - value: '.elser_model_2_linux-x86_64', - }, - { - label: '.multilingual-e5-small', - value: '.multilingual-e5-small', - }, - { - label: '.multilingual-e5-small_linux-x86_64', - value: '.multilingual-e5-small_linux-x86_64', - }, - ], - value: null, - ui_restrictions: [], - default_value: '.multilingual-e5-small', - depends_on: [], - }, - num_allocations: { - display: DisplayType.NUMERIC, - label: 'Number allocations', - order: 2, - required: true, - sensitive: false, - tooltip: - 'The total number of allocations this model is assigned across machine learning nodes.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 1, - depends_on: [], - }, - num_threads: { - display: DisplayType.NUMERIC, - label: 'Number threads', - order: 3, - required: true, - sensitive: false, - tooltip: 'Sets the number of threads used by each model allocation during inference.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: 2, - depends_on: [], - }, - }, - }, - { - provider: 'cohere', - logo: '', // should be cohere logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding', 'rerank'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'azureopenai', - logo: '', // should be azureopenai logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: false, - sensitive: true, - tooltip: `You must provide either an API key or an Entra ID.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - entra_id: { - display: DisplayType.TEXTBOX, - label: 'Entra ID', - order: 2, - required: false, - sensitive: true, - tooltip: `You must provide either an API key or an Entra ID.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - resource_name: { - display: DisplayType.TEXTBOX, - label: 'Resource Name', - order: 3, - required: true, - sensitive: false, - tooltip: `The name of your Azure OpenAI resource`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - api_version: { - display: DisplayType.TEXTBOX, - label: 'API version', - order: 4, - required: true, - sensitive: false, - tooltip: 'The Azure API version ID to use.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - deployment_id: { - display: DisplayType.TEXTBOX, - label: 'Deployment ID', - order: 5, - required: true, - sensitive: false, - tooltip: 'The deployment name of your deployed models.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'The azureopenai service sets a default number of requests allowed per minute depending on the task type.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'azureaistudio', - logo: '', // should be azureaistudio logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'text_embedding'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - target: { - display: DisplayType.TEXTBOX, - label: 'Target', - order: 2, - required: true, - sensitive: false, - tooltip: `The target URL of your Azure AI Studio model deployment.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - endpoint_type: { - display: DisplayType.DROPDOWN, - label: 'Endpoint type', - order: 3, - required: true, - sensitive: false, - tooltip: 'Specifies the type of endpoint that is used in your model deployment.', - type: FieldType.STRING, - options: [ - { - label: 'token', - value: 'token', - }, - { - label: 'realtime', - value: 'realtime', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - provider: { - display: DisplayType.DROPDOWN, - label: 'Provider', - order: 3, - required: true, - options: [ - { - label: 'cohere', - value: 'cohere', - }, - { - label: 'meta', - value: 'meta', - }, - { - label: 'microsoft_phi', - value: 'microsoft_phi', - }, - { - label: 'mistral', - value: 'mistral', - }, - { - label: 'openai', - value: 'openai', - }, - { - label: 'databricks', - value: 'databricks', - }, - ], - sensitive: false, - tooltip: 'The model provider for your deployment.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'anthropic', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['completion'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: `API Key for the provider you're connecting to`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 2, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 5, - required: false, - sensitive: false, - tooltip: - 'By default, the anthropic service sets the number of requests allowed per minute to 50.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'watsonxai', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['text_embedding'], - configuration: { - api_version: { - display: DisplayType.TEXTBOX, - label: 'API version', - order: 1, - required: true, - sensitive: false, - tooltip: 'The IBM Watsonx API version ID to use.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - project_id: { - display: DisplayType.TEXTBOX, - label: 'Project ID', - order: 2, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - model_id: { - display: DisplayType.TEXTBOX, - label: 'Model ID', - order: 3, - required: true, - sensitive: false, - tooltip: `The name of the model to use for the inference task.`, - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - url: { - display: DisplayType.TEXTBOX, - label: 'URL', - order: 4, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - max_input_tokens: { - display: DisplayType.NUMERIC, - label: 'Maximum input tokens', - order: 5, - required: false, - sensitive: false, - tooltip: 'Allows you to specify the maximum number of tokens per input.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - { - provider: 'alibabacloud-ai-search', - logo: '', // should be anthropic logo here, the hardcoded uses assets/images - taskTypes: ['completion', 'sparse_embedding', 'text_embedding', 'rerank'], - configuration: { - api_key: { - display: DisplayType.TEXTBOX, - label: 'API Key', - order: 1, - required: true, - sensitive: true, - tooltip: 'A valid API key for the AlibabaCloud AI Search API.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - service_id: { - display: DisplayType.DROPDOWN, - label: 'Project ID', - order: 2, - required: true, - sensitive: false, - tooltip: 'The name of the model service to use for the {infer} task.', - type: FieldType.STRING, - options: [ - { - label: 'ops-text-embedding-001', - value: 'ops-text-embedding-001', - }, - { - label: 'ops-text-embedding-zh-001', - value: 'ops-text-embedding-zh-001', - }, - { - label: 'ops-text-embedding-en-001', - value: 'ops-text-embedding-en-001', - }, - { - label: 'ops-text-embedding-002', - value: 'ops-text-embedding-002', - }, - { - label: 'ops-text-sparse-embedding-001', - value: 'ops-text-sparse-embedding-001', - }, - { - label: 'ops-bge-reranker-larger', - value: 'ops-bge-reranker-larger', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - host: { - display: DisplayType.TEXTBOX, - label: 'Host', - order: 3, - required: true, - sensitive: false, - tooltip: - 'The name of the host address used for the {infer} task. You can find the host address at https://opensearch.console.aliyun.com/cn-shanghai/rag/api-key[ the API keys section] of the documentation.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - http_schema: { - display: DisplayType.DROPDOWN, - label: 'HTTP Schema', - order: 4, - required: true, - sensitive: false, - tooltip: '', - type: FieldType.STRING, - options: [ - { - label: 'https', - value: 'https', - }, - { - label: 'http', - value: 'http', - }, - ], - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - workspace: { - display: DisplayType.TEXTBOX, - label: 'Workspace', - order: 5, - required: true, - sensitive: false, - tooltip: 'The name of the workspace used for the {infer} task.', - type: FieldType.STRING, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - 'rate_limit.requests_per_minute': { - display: DisplayType.NUMERIC, - label: 'Rate limit', - order: 6, - required: false, - sensitive: false, - tooltip: 'Minimize the number of rate limit errors.', - type: FieldType.INTEGER, - validations: [], - value: null, - ui_restrictions: [], - default_value: null, - depends_on: [], - }, - }, - }, - ] as InferenceProvider[]; - return Promise.resolve( - providers.sort((a, b) => (a.provider > b.provider ? 1 : b.provider > a.provider ? -1 : 0)) - ); +export const getProviders = async (http: HttpSetup): Promise => { + return await http.get(`${INTERNAL_BASE_STACK_CONNECTORS_API_PATH}/_inference/_services`); }; export const useProviders = (http: HttpSetup, toasts: ToastsStart) => { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx index 5d2c99ffd92c..5eb8518a5ea1 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx @@ -26,7 +26,7 @@ interface ServiceProviderProps { searchValue?: string; } -type ProviderSolution = 'Observability' | 'Security' | 'Search'; +export type ProviderSolution = 'Observability' | 'Security' | 'Search'; interface ServiceProviderRecord { icon: string; @@ -107,9 +107,7 @@ export const ServiceProviderIcon: React.FC = ({ providerKe return provider ? ( - ) : ( - {providerKey} - ); + ) : null; }; export const ServiceProviderName: React.FC = ({ diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx index d4527e9c7b9a..fc31c9dd6c4f 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx @@ -11,6 +11,7 @@ import React, { memo, useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { ServiceProviderKeys } from '../../../../../common/inference/constants'; import { + ProviderSolution, SERVICE_PROVIDERS, ServiceProviderIcon, ServiceProviderName, @@ -47,7 +48,20 @@ const SelectableProviderComponent: React.FC = ({ const renderProviderOption = useCallback>( (option, searchValue) => { - const provider = SERVICE_PROVIDERS[option.label as ServiceProviderKeys]; + const provider = Object.keys(SERVICE_PROVIDERS).includes(option.label) + ? SERVICE_PROVIDERS[option.label as ServiceProviderKeys] + : undefined; + + const supportedBySolutions = (provider && + provider.solutions.map((solution) => ( + + {solution} + + ))) ?? ( + + {'Search' as ProviderSolution} + + ); return ( @@ -65,12 +79,7 @@ const SelectableProviderComponent: React.FC = ({ - {provider && - provider.solutions.map((solution) => ( - - {solution} - - ))} + {supportedBySolutions} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts index 150292894b64..1bd55793bc46 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts @@ -14,7 +14,6 @@ import { SparseEmbeddingParams, TextEmbeddingParams, } from '../../../common/inference/types'; -import { ConfigProperties } from '../lib/dynamic_config/types'; export type InferenceActionParams = | { subAction: SUB_ACTION.COMPLETION; subActionParams: ChatCompleteParams } @@ -22,8 +21,6 @@ export type InferenceActionParams = | { subAction: SUB_ACTION.SPARSE_EMBEDDING; subActionParams: SparseEmbeddingParams } | { subAction: SUB_ACTION.TEXT_EMBEDDING; subActionParams: TextEmbeddingParams }; -export type FieldsConfiguration = Record; - export interface Config { taskType: string; taskTypeConfig?: Record; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx index 79ae552a9528..5560c831c4a6 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx @@ -25,12 +25,12 @@ import { } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; +import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types'; import { ensureBooleanType, ensureCorrectTyping, ensureStringType, } from './connector_configuration_utils'; -import { ConfigEntryView, DisplayType } from './types'; interface ConnectorConfigurationFieldProps { configEntry: ConfigEntryView; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx index 3190ac80275f..a7063d81719a 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ConfigEntryView, DisplayType } from './types'; +import { ConfigEntryView, DisplayType } from '../../../../common/dynamic_config/types'; import { ConnectorConfigurationField } from './connector_configuration_field'; interface ConnectorConfigurationFormItemsProps { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts index 182327a180a6..cce5bc15fa56 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConfigProperties, FieldType } from './types'; +import { ConfigProperties, FieldType } from '../../../../common/dynamic_config/types'; export type ConnectorConfigEntry = ConfigProperties & { key: string }; diff --git a/x-pack/plugins/stack_connectors/server/plugin.ts b/x-pack/plugins/stack_connectors/server/plugin.ts index aee84d963043..b20892938735 100644 --- a/x-pack/plugins/stack_connectors/server/plugin.ts +++ b/x-pack/plugins/stack_connectors/server/plugin.ts @@ -8,7 +8,11 @@ import { PluginInitializerContext, Plugin, CoreSetup, Logger } from '@kbn/core/server'; import { PluginSetupContract as ActionsPluginSetupContract } from '@kbn/actions-plugin/server'; import { registerConnectorTypes } from './connector_types'; -import { validSlackApiChannelsRoute, getWellKnownEmailServiceRoute } from './routes'; +import { + validSlackApiChannelsRoute, + getWellKnownEmailServiceRoute, + getInferenceServicesRoute, +} from './routes'; import { ExperimentalFeatures, parseExperimentalConfigValue, @@ -39,6 +43,7 @@ export class StackConnectorsPlugin implements Plugin { getWellKnownEmailServiceRoute(router); validSlackApiChannelsRoute(router, actions.getActionsConfigurationUtilities(), this.logger); + getInferenceServicesRoute(router); registerConnectorTypes({ actions, diff --git a/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts new file mode 100644 index 000000000000..50596028d80a --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock, httpServerMock } from '@kbn/core/server/mocks'; +import { coreMock } from '@kbn/core/server/mocks'; +import { getInferenceServicesRoute } from './get_inference_services'; +import { DisplayType, FieldType } from '../../common/dynamic_config/types'; + +describe('getInferenceServicesRoute', () => { + it('returns available service providers', async () => { + const router = httpServiceMock.createRouter(); + const core = coreMock.createRequestHandlerContext(); + + const mockResult = [ + { + provider: 'openai', + task_types: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 3, + required: true, + sensitive: true, + tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: 'The name of the model to use for the inference task.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + organization_id: { + display: DisplayType.TEXTBOX, + label: 'Organization ID', + order: 4, + required: false, + sensitive: false, + tooltip: 'The unique identifier of your organization.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 1, + required: true, + sensitive: false, + tooltip: + 'The OpenAI API endpoint URL. For more information on the URL, refer to the https://platform.openai.com/docs/api-reference.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: 'https://api.openai.com/v1/chat/completions', + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: + 'Default number of requests allowed per minute. For text_embedding is 3000. For completion is 500.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ]; + core.elasticsearch.client.asInternalUser.transport.request.mockResolvedValue(mockResult); + + getInferenceServicesRoute(router); + + const [config, handler] = router.get.mock.calls[0]; + expect(config.path).toMatchInlineSnapshot(`"/internal/stack_connectors/_inference/_services"`); + + const mockResponse = httpServerMock.createResponseFactory(); + const mockRequest = httpServerMock.createKibanaRequest(); + await handler({ core }, mockRequest, mockResponse); + + expect(mockResponse.ok).toHaveBeenCalledWith({ + body: mockResult, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts new file mode 100644 index 000000000000..139607283426 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from '@kbn/core/server'; +import { InferenceProvider } from '../../common/inference/types'; +import { INTERNAL_BASE_STACK_CONNECTORS_API_PATH } from '../../common'; + +export const getInferenceServicesRoute = (router: IRouter) => { + router.get( + { + path: `${INTERNAL_BASE_STACK_CONNECTORS_API_PATH}/_inference/_services`, + options: { + access: 'internal', + }, + validate: false, + }, + handler + ); + + async function handler( + ctx: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise { + const esClient = (await ctx.core).elasticsearch.client.asInternalUser; + + const response = await esClient.transport.request<{ + endpoints: InferenceProvider[]; + }>({ + method: 'GET', + path: `/_inference/_services`, + }); + + return res.ok({ + body: response, + }); + } +}; diff --git a/x-pack/plugins/stack_connectors/server/routes/index.ts b/x-pack/plugins/stack_connectors/server/routes/index.ts index cd9857b2168e..e64995e1a50e 100644 --- a/x-pack/plugins/stack_connectors/server/routes/index.ts +++ b/x-pack/plugins/stack_connectors/server/routes/index.ts @@ -7,3 +7,4 @@ export { getWellKnownEmailServiceRoute } from './get_well_known_email_service'; export { validSlackApiChannelsRoute } from './valid_slack_api_channels'; +export { getInferenceServicesRoute } from './get_inference_services'; From 6e5f793c489e23e41d372f18cc22ab363b9724f4 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:26:12 +1100 Subject: [PATCH 036/100] [api-docs] 2024-11-12 Daily api_docs build (#199729) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/889 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- .../ai_assistant_management_selection.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/apm_data_access.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_quality.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_usage.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/dataset_quality.mdx | 2 +- api_docs/deprecations_by_api.mdx | 3 +- api_docs/deprecations_by_plugin.mdx | 3 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/discover_shared.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/elastic_assistant.devdocs.json | 21 + api_docs/elastic_assistant.mdx | 4 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/entities_data_access.mdx | 2 +- api_docs/entity_manager.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/esql.mdx | 2 +- api_docs/esql_data_grid.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_annotation_listing.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/fields_metadata.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- .../index_lifecycle_management.devdocs.json | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/inference.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/ingest_pipelines.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/integration_assistant.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/inventory.mdx | 2 +- api_docs/investigate.mdx | 2 +- api_docs/investigate_app.mdx | 2 +- api_docs/kbn_actions_types.mdx | 2 +- api_docs/kbn_ai_assistant.mdx | 2 +- api_docs/kbn_ai_assistant_common.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_log_pattern_analysis.mdx | 2 +- api_docs/kbn_aiops_log_rate_analysis.mdx | 2 +- .../kbn_alerting_api_integration_helpers.mdx | 2 +- api_docs/kbn_alerting_comparators.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerting_types.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_grouping.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_collection_utils.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_data_view.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_types.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_avc_banner.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_bfetch_error.mdx | 2 +- api_docs/kbn_calculate_auto.mdx | 2 +- .../kbn_calculate_width_from_char_count.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cbor.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_cloud_security_posture.mdx | 2 +- .../kbn_cloud_security_posture_common.mdx | 2 +- api_docs/kbn_cloud_security_posture_graph.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mock.mdx | 2 +- api_docs/kbn_code_owners.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- ...ent_management_content_insights_public.mdx | 2 +- ...ent_management_content_insights_server.mdx | 2 +- ...bn_content_management_favorites_public.mdx | 2 +- ...bn_content_management_favorites_server.mdx | 2 +- ...tent_management_tabbed_table_list_view.mdx | 2 +- ...kbn_content_management_table_list_view.mdx | 2 +- ...tent_management_table_list_view_common.mdx | 2 +- ...ntent_management_table_list_view_table.mdx | 2 +- .../kbn_content_management_user_profiles.mdx | 2 +- api_docs/kbn_content_management_utils.mdx | 2 +- .../kbn_core_analytics_browser.devdocs.json | 168 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- .../kbn_core_analytics_server.devdocs.json | 168 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.devdocs.json | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ..._core_custom_branding_browser_internal.mdx | 2 +- ...kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...n_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_feature_flags_browser.mdx | 2 +- ...bn_core_feature_flags_browser_internal.mdx | 2 +- .../kbn_core_feature_flags_browser_mocks.mdx | 2 +- api_docs/kbn_core_feature_flags_server.mdx | 2 +- ...kbn_core_feature_flags_server_internal.mdx | 2 +- .../kbn_core_feature_flags_server_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.devdocs.json | 8 + api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- .../kbn_core_plugins_contracts_browser.mdx | 2 +- .../kbn_core_plugins_contracts_server.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_security_browser.mdx | 2 +- .../kbn_core_security_browser_internal.mdx | 2 +- api_docs/kbn_core_security_browser_mocks.mdx | 2 +- api_docs/kbn_core_security_common.mdx | 2 +- api_docs/kbn_core_security_server.mdx | 2 +- .../kbn_core_security_server_internal.mdx | 2 +- api_docs/kbn_core_security_server_mocks.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- .../kbn_core_test_helpers_model_versions.mdx | 2 +- ...n_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_browser.mdx | 2 +- ...kbn_core_user_profile_browser_internal.mdx | 2 +- .../kbn_core_user_profile_browser_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_common.mdx | 2 +- api_docs/kbn_core_user_profile_server.mdx | 2 +- .../kbn_core_user_profile_server_internal.mdx | 2 +- .../kbn_core_user_profile_server_mocks.mdx | 2 +- api_docs/kbn_core_user_settings_server.mdx | 2 +- .../kbn_core_user_settings_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_custom_icons.mdx | 2 +- api_docs/kbn_custom_integrations.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_data_forge.mdx | 2 +- api_docs/kbn_data_service.mdx | 2 +- api_docs/kbn_data_stream_adapter.mdx | 2 +- api_docs/kbn_data_view_utils.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_deeplinks_analytics.mdx | 2 +- api_docs/kbn_deeplinks_devtools.mdx | 2 +- api_docs/kbn_deeplinks_fleet.mdx | 2 +- api_docs/kbn_deeplinks_management.mdx | 2 +- api_docs/kbn_deeplinks_ml.mdx | 2 +- api_docs/kbn_deeplinks_observability.mdx | 2 +- api_docs/kbn_deeplinks_search.devdocs.json | 2 +- api_docs/kbn_deeplinks_search.mdx | 2 +- api_docs/kbn_deeplinks_security.mdx | 2 +- api_docs/kbn_deeplinks_shared.mdx | 2 +- api_docs/kbn_default_nav_analytics.mdx | 2 +- api_docs/kbn_default_nav_devtools.mdx | 2 +- api_docs/kbn_default_nav_management.mdx | 2 +- api_docs/kbn_default_nav_ml.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- ...iscover_contextual_components.devdocs.json | 4 +- .../kbn_discover_contextual_components.mdx | 2 +- api_docs/kbn_discover_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_elastic_agent_utils.mdx | 2 +- api_docs/kbn_elastic_assistant.mdx | 2 +- api_docs/kbn_elastic_assistant_common.mdx | 2 +- api_docs/kbn_entities_schema.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.devdocs.json | 4 + api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_esql_ast.mdx | 2 +- api_docs/kbn_esql_editor.mdx | 2 +- api_docs/kbn_esql_utils.mdx | 2 +- api_docs/kbn_esql_validation_autocomplete.mdx | 2 +- api_docs/kbn_event_annotation_common.mdx | 2 +- api_docs/kbn_event_annotation_components.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_field_utils.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_formatters.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- .../kbn_ftr_common_functional_ui_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_console_definitions.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_grid_layout.mdx | 2 +- api_docs/kbn_grouping.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- ...ycle_management_common_shared.devdocs.json | 1447 +++++++++++++++++ ...dex_lifecycle_management_common_shared.mdx | 33 + .../kbn_index_management_shared_types.mdx | 2 +- api_docs/kbn_inference_common.mdx | 2 +- api_docs/kbn_inference_integration_flyout.mdx | 2 +- api_docs/kbn_infra_forge.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_investigation_shared.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_ipynb.mdx | 2 +- api_docs/kbn_item_buffer.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_json_schemas.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_language_documentation.mdx | 2 +- api_docs/kbn_lens_embeddable_utils.mdx | 2 +- api_docs/kbn_lens_formula_docs.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_content_badge.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_management_cards_navigation.mdx | 2 +- .../kbn_management_settings_application.mdx | 2 +- ...ent_settings_components_field_category.mdx | 2 +- ...gement_settings_components_field_input.mdx | 2 +- ...nagement_settings_components_field_row.mdx | 2 +- ...bn_management_settings_components_form.mdx | 2 +- ...n_management_settings_field_definition.mdx | 2 +- .../kbn_management_settings_ids.devdocs.json | 15 - api_docs/kbn_management_settings_ids.mdx | 4 +- ...n_management_settings_section_registry.mdx | 2 +- api_docs/kbn_management_settings_types.mdx | 2 +- .../kbn_management_settings_utilities.mdx | 2 +- api_docs/kbn_management_storybook_config.mdx | 2 +- api_docs/kbn_manifest.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_maps_vector_tile_utils.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_anomaly_utils.mdx | 2 +- api_docs/kbn_ml_cancellable_search.mdx | 2 +- api_docs/kbn_ml_category_validator.mdx | 2 +- api_docs/kbn_ml_chi2test.mdx | 2 +- .../kbn_ml_data_frame_analytics_utils.mdx | 2 +- api_docs/kbn_ml_data_grid.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_date_utils.mdx | 2 +- api_docs/kbn_ml_error_utils.mdx | 2 +- api_docs/kbn_ml_field_stats_flyout.mdx | 2 +- api_docs/kbn_ml_in_memory_table.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_kibana_theme.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.mdx | 2 +- api_docs/kbn_ml_parse_interval.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_runtime_field_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_time_buckets.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_ui_actions.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_ml_validators.mdx | 2 +- api_docs/kbn_mock_idp_utils.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_object_versioning_utils.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- .../kbn_observability_alerting_rule_utils.mdx | 2 +- .../kbn_observability_alerting_test_data.mdx | 2 +- ...ility_get_padded_alert_time_range_util.mdx | 2 +- api_docs/kbn_observability_logs_overview.mdx | 2 +- ...kbn_observability_synthetics_test_data.mdx | 2 +- api_docs/kbn_openapi_bundler.mdx | 2 +- api_docs/kbn_openapi_generator.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_panel_loader.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_check.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_presentation_containers.mdx | 2 +- api_docs/kbn_presentation_publishing.mdx | 2 +- api_docs/kbn_product_doc_artifact_builder.mdx | 2 +- api_docs/kbn_profiling_utils.mdx | 2 +- api_docs/kbn_random_sampling.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_react_hooks.mdx | 2 +- api_docs/kbn_react_kibana_context_common.mdx | 2 +- api_docs/kbn_react_kibana_context_render.mdx | 2 +- api_docs/kbn_react_kibana_context_root.mdx | 2 +- api_docs/kbn_react_kibana_context_styled.mdx | 2 +- api_docs/kbn_react_kibana_context_theme.mdx | 2 +- api_docs/kbn_react_kibana_mount.mdx | 2 +- api_docs/kbn_recently_accessed.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.mdx | 2 +- api_docs/kbn_reporting_csv_share_panel.mdx | 2 +- api_docs/kbn_reporting_export_types_csv.mdx | 2 +- .../kbn_reporting_export_types_csv_common.mdx | 2 +- api_docs/kbn_reporting_export_types_pdf.mdx | 2 +- .../kbn_reporting_export_types_pdf_common.mdx | 2 +- api_docs/kbn_reporting_export_types_png.mdx | 2 +- .../kbn_reporting_export_types_png_common.mdx | 2 +- api_docs/kbn_reporting_mocks_server.mdx | 2 +- api_docs/kbn_reporting_public.mdx | 2 +- api_docs/kbn_reporting_server.mdx | 2 +- api_docs/kbn_resizable_layout.mdx | 2 +- .../kbn_response_ops_feature_flag_service.mdx | 2 +- api_docs/kbn_response_ops_rule_params.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rollup.mdx | 2 +- api_docs/kbn_router_to_openapispec.mdx | 2 +- api_docs/kbn_router_utils.mdx | 2 +- api_docs/kbn_rrule.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_screenshotting_server.mdx | 2 +- api_docs/kbn_search_api_keys_components.mdx | 2 +- api_docs/kbn_search_api_keys_server.mdx | 2 +- api_docs/kbn_search_api_panels.mdx | 2 +- api_docs/kbn_search_connectors.mdx | 2 +- api_docs/kbn_search_errors.mdx | 2 +- api_docs/kbn_search_index_documents.mdx | 2 +- api_docs/kbn_search_response_warnings.mdx | 2 +- api_docs/kbn_search_shared_ui.mdx | 2 +- api_docs/kbn_search_types.mdx | 2 +- api_docs/kbn_security_api_key_management.mdx | 2 +- api_docs/kbn_security_authorization_core.mdx | 2 +- ...kbn_security_authorization_core_common.mdx | 2 +- api_docs/kbn_security_form_components.mdx | 2 +- api_docs/kbn_security_hardening.mdx | 2 +- api_docs/kbn_security_plugin_types_common.mdx | 2 +- api_docs/kbn_security_plugin_types_public.mdx | 2 +- api_docs/kbn_security_plugin_types_server.mdx | 2 +- .../kbn_security_role_management_model.mdx | 2 +- ...kbn_security_solution_distribution_bar.mdx | 2 +- api_docs/kbn_security_solution_features.mdx | 2 +- api_docs/kbn_security_solution_navigation.mdx | 2 +- api_docs/kbn_security_solution_side_nav.mdx | 2 +- ...kbn_security_solution_storybook_config.mdx | 2 +- api_docs/kbn_security_ui_components.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_data_table.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- .../kbn_server_route_repository_client.mdx | 2 +- .../kbn_server_route_repository_utils.mdx | 2 +- api_docs/kbn_serverless_common_settings.mdx | 2 +- .../kbn_serverless_observability_settings.mdx | 2 +- api_docs/kbn_serverless_project_switcher.mdx | 2 +- api_docs/kbn_serverless_search_settings.mdx | 2 +- api_docs/kbn_serverless_security_settings.mdx | 2 +- api_docs/kbn_serverless_storybook_config.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_chrome_navigation.mdx | 2 +- api_docs/kbn_shared_ux_error_boundary.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_tabbed_modal.mdx | 2 +- api_docs/kbn_shared_ux_table_persist.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_predicates.mdx | 2 +- api_docs/kbn_sse_utils.mdx | 2 +- api_docs/kbn_sse_utils_client.mdx | 2 +- api_docs/kbn_sse_utils_server.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_synthetics_e2e.mdx | 2 +- api_docs/kbn_synthetics_private_location.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_eui_helpers.mdx | 2 +- api_docs/kbn_test_jest_helpers.devdocs.json | 4 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_timerange.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_transpose_utils.mdx | 2 +- api_docs/kbn_triggers_actions_ui_types.mdx | 2 +- api_docs/kbn_try_in_console.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_unified_data_table.mdx | 2 +- api_docs/kbn_unified_doc_viewer.mdx | 2 +- api_docs/kbn_unified_field_list.mdx | 2 +- api_docs/kbn_unsaved_changes_badge.mdx | 2 +- api_docs/kbn_unsaved_changes_prompt.mdx | 2 +- api_docs/kbn_use_tracked_promise.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_visualization_ui_components.mdx | 2 +- api_docs/kbn_visualization_utils.mdx | 2 +- api_docs/kbn_xstate_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kbn_zod.mdx | 2 +- api_docs/kbn_zod_helpers.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/links.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/logs_data_access.mdx | 2 +- api_docs/logs_explorer.mdx | 2 +- api_docs/logs_shared.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/metrics_data_access.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/mock_idp_plugin.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/no_data_page.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.devdocs.json | 8 +- api_docs/observability.mdx | 2 +- api_docs/observability_a_i_assistant.mdx | 2 +- api_docs/observability_a_i_assistant_app.mdx | 2 +- .../observability_ai_assistant_management.mdx | 2 +- api_docs/observability_logs_explorer.mdx | 2 +- api_docs/observability_onboarding.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/painless_lab.mdx | 2 +- api_docs/plugin_directory.mdx | 15 +- api_docs/presentation_panel.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/profiling_data_access.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/search_assistant.mdx | 2 +- api_docs/search_connectors.mdx | 2 +- api_docs/search_homepage.mdx | 2 +- api_docs/search_indices.devdocs.json | 2 +- api_docs/search_indices.mdx | 2 +- api_docs/search_inference_endpoints.mdx | 2 +- api_docs/search_notebooks.mdx | 2 +- api_docs/search_playground.devdocs.json | 15 +- api_docs/search_playground.mdx | 7 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/security_solution_ess.mdx | 2 +- api_docs/security_solution_serverless.mdx | 2 +- api_docs/serverless.mdx | 2 +- api_docs/serverless_observability.mdx | 2 +- api_docs/serverless_search.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/slo.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_doc_viewer.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/uptime.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.devdocs.json | 14 - api_docs/visualizations.mdx | 4 +- 774 files changed, 2356 insertions(+), 1101 deletions(-) create mode 100644 api_docs/kbn_index_lifecycle_management_common_shared.devdocs.json create mode 100644 api_docs/kbn_index_lifecycle_management_common_shared.mdx diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index b8e1e2f95140..8213690c68bb 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 00759b204646..6ff23feb1abe 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 55226b95b7e3..903d7bb8564d 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 51ff2b2177fb..2dbb911712af 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index a226470d4e6c..f1c95bf42eee 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index bda18816dc5a..d0518c5574ce 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 1567922bf310..76949f438879 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index e8d4cbfc2e8d..4c8900cf3582 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 97275e9fa208..a9e8cc610f7b 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 4abcc9b29d10..477390c3afae 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 18f0eec76147..dd96643533e4 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index b244870c6ed5..52e0f0e1b1e7 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 981162c665fe..05c7d8e00488 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 96a185b5ecda..edb6f7038849 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index dcf04fa9d521..3d811c99930f 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 6054ff7c3b44..eb5333457325 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 0910343b0ba5..0d6bbaaeed5f 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 3d2e18a61a11..458f4f5f7107 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index cb2b856eeebd..eb98a295545b 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 7f4299632010..bb64c3093d65 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 057695d3c3f8..d16af22e2b35 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 38972a956d97..d751d31185d1 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 5bd3d5064312..0951e09a7e45 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index 8219e0f0e96a..0d5b37eda3c0 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index edb8c7449f51..a524ed87dd39 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 4b2a71c75be0..4801aeb14687 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_usage.mdx b/api_docs/data_usage.mdx index 112703ea492e..ca09f822938c 100644 --- a/api_docs/data_usage.mdx +++ b/api_docs/data_usage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataUsage title: "dataUsage" image: https://source.unsplash.com/400x175/?github description: API docs for the dataUsage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataUsage'] --- import dataUsageObj from './data_usage.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index da326b4c23f5..287eeb2826c4 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 4c844be76ca4..97d8efb2e4f7 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 3cd0e5edcb0a..d9bfe3d16257 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 4a6d3e0cc8aa..e06438b19b31 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 0a7c4debd41f..92e6d765b771 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 9d1c36d907a7..781077b90fbd 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index a33d8fa45cb8..b8625def2dbb 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -158,6 +158,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | security | - | | | observabilityShared | - | | | @kbn/react-kibana-context-styled, kibanaReact | - | +| | indexLifecycleManagement | - | | | @kbn/reporting-public, discover | - | | | discover, @kbn/management-settings-field-definition | - | | | @kbn/content-management-table-list-view, filesManagement | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 77c822765565..efc3f2961fa6 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -944,6 +944,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [license.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/server/services/license.ts#:~:text=license%24) | 8.8.0 | +| | [mocks.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/__jest__/mocks.ts#:~:text=max_size), [deserializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts#:~:text=max_size), [deserializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts#:~:text=max_size), [deserializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts#:~:text=max_size), [serializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts#:~:text=max_size), [serializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts#:~:text=max_size), [serializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts#:~:text=max_size), [serializer.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts#:~:text=max_size), [rollover.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/policy_flyout/components/rollover.tsx#:~:text=max_size), [rollover.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/policy_flyout/components/rollover.tsx#:~:text=max_size)+ 7 more | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 44e1d57c4d6b..5c53f577e6b0 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 2a5a9fd0b501..4b27de358490 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index ec8ee3156341..e160bb054f6e 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 2678f6133ba7..29651f5536a5 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index 4329bc6ffc13..e739cfef4d20 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index e7e55ffc348e..a5890f386a49 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.devdocs.json b/api_docs/elastic_assistant.devdocs.json index 4df33db88b4d..3fe39450a7c8 100644 --- a/api_docs/elastic_assistant.devdocs.json +++ b/api_docs/elastic_assistant.devdocs.json @@ -1683,6 +1683,27 @@ "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.telemetry", + "type": "Object", + "tags": [], + "label": "telemetry", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-analytics-server", + "scope": "server", + "docId": "kibKbnCoreAnalyticsServerPluginApi", + "section": "def-server.AnalyticsServiceSetup", + "text": "AnalyticsServiceSetup" + }, + " | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 219732e07f1a..d15d05811d12 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 37 | 2 | +| 53 | 0 | 38 | 2 | ## Server diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index c2b12a40e8e2..99fe95318c4d 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 2fcf287eeea7..38e3f8964dbf 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 34902ffc8754..01184c77221c 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 4c644bbd2a7f..6e845c5eacbc 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entities_data_access.mdx b/api_docs/entities_data_access.mdx index 96d31ff61676..3aa1ca62de52 100644 --- a/api_docs/entities_data_access.mdx +++ b/api_docs/entities_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entitiesDataAccess title: "entitiesDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the entitiesDataAccess plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entitiesDataAccess'] --- import entitiesDataAccessObj from './entities_data_access.devdocs.json'; diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index 80d39444e3f2..6eed6037e559 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 6b499b4ec061..dcbbd723d478 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx index c2bd5fb7c288..9ba78a757613 100644 --- a/api_docs/esql.mdx +++ b/api_docs/esql.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esql title: "esql" image: https://source.unsplash.com/400x175/?github description: API docs for the esql plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] --- import esqlObj from './esql.devdocs.json'; diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index 0a11cabffc49..4127d6baa15d 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 3dad1639c344..4b25969e7239 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 2d89eb0a36e3..253efc5daaec 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 562392882381..3e9e78585d71 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index ad594d8eae24..6a8a5f76c162 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 3fa12da3b6f4..eebaeb3c69fc 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index a57baa99df7e..fc1020122f57 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index dc96b1dca423..91104c370d99 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 2b9d36efb1cd..ed1c4f3b3682 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 062a359c0704..f020c2fbac32 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index e228884ecf8e..7b41cedd0de5 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 9995780f2d10..e26c63b99c97 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index d6778f916fd7..c9b73f9a57e9 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 2e9dd01c5b8b..c77344c8f7da 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 602aa5d157db..5a6f58e76de9 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 6a548c164d4f..f4e196a5756b 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 49b120429a5d..379cad7b3b68 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index f1d557a4bf68..e98d44b5029a 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index fc5b5c3d3279..955e3bd1bb77 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index ea2d7c08dffc..0411c023a941 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 7dfcb56035a8..2152f8c9eb64 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index e6d3c3ad2d3c..182306396fd2 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 67163b1958f1..2ac8c1ad62ae 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index ca4b9fcd4b37..25d75fea904e 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index af60e318bbf0..e7259ba5ea61 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index f16b5191e384..f9f9b010bdb5 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 9b73fc69d72b..bd4663eb054b 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 14001b4740c0..e0a6f3f5dedd 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 12e511babd65..1750cc9c4839 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index afd91c27c691..b27ee510657c 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.devdocs.json b/api_docs/index_lifecycle_management.devdocs.json index 03825ec75860..d7ec7a91677c 100644 --- a/api_docs/index_lifecycle_management.devdocs.json +++ b/api_docs/index_lifecycle_management.devdocs.json @@ -76,7 +76,7 @@ "signature": [ "\"ILM_LOCATOR_ID\"" ], - "path": "x-pack/plugins/index_lifecycle_management/public/locator.ts", + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 9b94ace7a2c6..2ac0dcfaf98a 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 54c5753290ad..3cbc9017267c 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/inference.mdx b/api_docs/inference.mdx index cb96958e814e..5889e24b72ce 100644 --- a/api_docs/inference.mdx +++ b/api_docs/inference.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inference title: "inference" image: https://source.unsplash.com/400x175/?github description: API docs for the inference plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inference'] --- import inferenceObj from './inference.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index a7a9de549fbf..bb0c9357874c 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index b04c6138150d..8b52232dfdde 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index ef3c3275c8a1..27ac4506fff0 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index 1aafdf3098a8..ce73de5a54c4 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index d77b22bf5734..fc5f400d8fcf 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/inventory.mdx b/api_docs/inventory.mdx index 31f826ee9e7e..a990a02009d5 100644 --- a/api_docs/inventory.mdx +++ b/api_docs/inventory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inventory title: "inventory" image: https://source.unsplash.com/400x175/?github description: API docs for the inventory plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inventory'] --- import inventoryObj from './inventory.devdocs.json'; diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index 3bae92a3195a..741c6e1c5114 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.devdocs.json'; diff --git a/api_docs/investigate_app.mdx b/api_docs/investigate_app.mdx index f4f668b21400..f0ae814454ee 100644 --- a/api_docs/investigate_app.mdx +++ b/api_docs/investigate_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigateApp title: "investigateApp" image: https://source.unsplash.com/400x175/?github description: API docs for the investigateApp plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigateApp'] --- import investigateAppObj from './investigate_app.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 3a44e27c1752..323a0f2db579 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant.mdx b/api_docs/kbn_ai_assistant.mdx index 6fbd0bc24eae..b507eb315eda 100644 --- a/api_docs/kbn_ai_assistant.mdx +++ b/api_docs/kbn_ai_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant title: "@kbn/ai-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant'] --- import kbnAiAssistantObj from './kbn_ai_assistant.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant_common.mdx b/api_docs/kbn_ai_assistant_common.mdx index 42c0153a9c30..edacea1dc39f 100644 --- a/api_docs/kbn_ai_assistant_common.mdx +++ b/api_docs/kbn_ai_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant-common title: "@kbn/ai-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant-common'] --- import kbnAiAssistantCommonObj from './kbn_ai_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 1ffa0789a764..cc29b5ba5dfa 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index 3818bafb385b..6a83be2ef6cc 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 48e0fb0e69db..b6d2c0ec0a60 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 34544911e0f1..a0a848dc35bd 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index 52eef1d8bdca..73454eb6b033 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 70984e50600e..69d5ec23230a 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index cdcf09268a18..84c78d006a98 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 22f366956465..c52f50b5e995 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index 438f3260b4ff..9b7d661ebb62 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 71f7a2c838df..d57b0582b6a6 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 4540cb045209..b2ee2ae503c5 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index c816ae99747c..d82b56b24aeb 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index df615ee1d7c7..62d47b008ad5 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 798ccf69a5ec..065e099e353a 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index ab90bf6a85d0..9edda86620a6 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 48e740cc6c6d..b22847579b95 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_types.mdx b/api_docs/kbn_apm_types.mdx index 2042138edca0..f018b4dc4f25 100644 --- a/api_docs/kbn_apm_types.mdx +++ b/api_docs/kbn_apm_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-types title: "@kbn/apm-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-types'] --- import kbnApmTypesObj from './kbn_apm_types.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 2a2b9e51fedf..5b84cbd3e321 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_avc_banner.mdx b/api_docs/kbn_avc_banner.mdx index f4d4fb1ccafb..964a8d376ae0 100644 --- a/api_docs/kbn_avc_banner.mdx +++ b/api_docs/kbn_avc_banner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-avc-banner title: "@kbn/avc-banner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/avc-banner plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/avc-banner'] --- import kbnAvcBannerObj from './kbn_avc_banner.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index c4ef1fba843f..e93dd4df949e 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index c4ef81d688a4..3b4644bfed2b 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index bfe80a5f473f..e20024ebcc43 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index bb02d73d5683..4e2600cb02f1 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index d75f35e50861..e1f2327ed732 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cbor.mdx b/api_docs/kbn_cbor.mdx index d3ded1babe10..d183f1aefe14 100644 --- a/api_docs/kbn_cbor.mdx +++ b/api_docs/kbn_cbor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cbor title: "@kbn/cbor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cbor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cbor'] --- import kbnCborObj from './kbn_cbor.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 59bd08393986..320ae236f19c 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index fe7496fa5fc4..1804025bc6e2 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 5c61643fe28d..2abd21bd4910 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 4ecf45476665..f2165c99ee6a 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 6207ba4708f5..ca26b29a5504 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 1140ef95f915..0716b421c75d 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 54d2e9a9a1bd..6d608449b7e0 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture.mdx b/api_docs/kbn_cloud_security_posture.mdx index 2105d0f705b2..5a39b4863ce8 100644 --- a/api_docs/kbn_cloud_security_posture.mdx +++ b/api_docs/kbn_cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture title: "@kbn/cloud-security-posture" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture'] --- import kbnCloudSecurityPostureObj from './kbn_cloud_security_posture.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_common.mdx b/api_docs/kbn_cloud_security_posture_common.mdx index e0419e12a595..980f1e79d34c 100644 --- a/api_docs/kbn_cloud_security_posture_common.mdx +++ b/api_docs/kbn_cloud_security_posture_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-common title: "@kbn/cloud-security-posture-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-common'] --- import kbnCloudSecurityPostureCommonObj from './kbn_cloud_security_posture_common.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_graph.mdx b/api_docs/kbn_cloud_security_posture_graph.mdx index da7b99c4d816..a9807078c177 100644 --- a/api_docs/kbn_cloud_security_posture_graph.mdx +++ b/api_docs/kbn_cloud_security_posture_graph.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-graph title: "@kbn/cloud-security-posture-graph" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-graph plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-graph'] --- import kbnCloudSecurityPostureGraphObj from './kbn_cloud_security_posture_graph.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 2dbe9958c30a..353c1146fc6e 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 65fb7e960dc5..7b994f1c7fe5 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index adfab103c2a0..c0502db23115 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index bacd8d90cbd7..c38e34841f10 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 872211553bbf..36df67b11013 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 1347f9befa00..64dc10df0c3a 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 5d155ab7305d..b150c48b02ad 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 3722e6ade741..d23fabc9adf0 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_public.mdx b/api_docs/kbn_content_management_content_insights_public.mdx index 99edfc129929..1c65dad73ba5 100644 --- a/api_docs/kbn_content_management_content_insights_public.mdx +++ b/api_docs/kbn_content_management_content_insights_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-public title: "@kbn/content-management-content-insights-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-public plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-public'] --- import kbnContentManagementContentInsightsPublicObj from './kbn_content_management_content_insights_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_server.mdx b/api_docs/kbn_content_management_content_insights_server.mdx index 5082b4b2a6db..12a392966e5c 100644 --- a/api_docs/kbn_content_management_content_insights_server.mdx +++ b/api_docs/kbn_content_management_content_insights_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-server title: "@kbn/content-management-content-insights-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-server'] --- import kbnContentManagementContentInsightsServerObj from './kbn_content_management_content_insights_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_public.mdx b/api_docs/kbn_content_management_favorites_public.mdx index 5ce01aa6860d..1bd4a4c16c00 100644 --- a/api_docs/kbn_content_management_favorites_public.mdx +++ b/api_docs/kbn_content_management_favorites_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-public title: "@kbn/content-management-favorites-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-public plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-public'] --- import kbnContentManagementFavoritesPublicObj from './kbn_content_management_favorites_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_server.mdx b/api_docs/kbn_content_management_favorites_server.mdx index f99ace4c6a7c..1b8425c4c298 100644 --- a/api_docs/kbn_content_management_favorites_server.mdx +++ b/api_docs/kbn_content_management_favorites_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-server title: "@kbn/content-management-favorites-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-server'] --- import kbnContentManagementFavoritesServerObj from './kbn_content_management_favorites_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index f061d59f9db2..609458b5331d 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 8fddccb28f5e..dc11375e8a81 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index f1fdf635a4a7..c03a1ec3197c 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 75317cbb1806..db646bd36292 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index 59dc618b47ae..74795dd1d890 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 88be3d0829e7..d00fea0e055b 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.devdocs.json b/api_docs/kbn_core_analytics_browser.devdocs.json index 288f48a4955a..16e5bf6680c4 100644 --- a/api_docs/kbn_core_analytics_browser.devdocs.json +++ b/api_docs/kbn_core_analytics_browser.devdocs.json @@ -702,9 +702,17 @@ "plugin": "datasetQuality", "path": "x-pack/plugins/observability_solution/dataset_quality/public/services/telemetry/telemetry_client.ts" }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts" + }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts" + }, { "plugin": "elasticAssistant", - "path": "x-pack/plugins/elastic_assistant/server/routes/helpers.ts" + "path": "x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts" }, { "plugin": "elasticAssistant", @@ -722,6 +730,10 @@ "plugin": "elasticAssistant", "path": "x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/handle_graph_error/index.tsx" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts" + }, { "plugin": "globalSearchBar", "path": "x-pack/plugins/global_search_bar/public/telemetry/event_reporter.ts" @@ -796,151 +808,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts" }, { "plugin": "osquery", @@ -1170,6 +1038,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts" }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts" + }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts" + }, { "plugin": "@kbn/shared-ux-chrome-navigation", "path": "packages/shared-ux/chrome/navigation/mocks/storybook.ts" diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index c777392ba617..9259732156ac 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index e39d17bc3110..aa45f95180ae 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 0de9f23aa2f0..c0bd6d0cd571 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.devdocs.json b/api_docs/kbn_core_analytics_server.devdocs.json index c08c5dec164f..f4eedf0c5ca7 100644 --- a/api_docs/kbn_core_analytics_server.devdocs.json +++ b/api_docs/kbn_core_analytics_server.devdocs.json @@ -710,9 +710,17 @@ "plugin": "datasetQuality", "path": "x-pack/plugins/observability_solution/dataset_quality/public/services/telemetry/telemetry_client.ts" }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.ts" + }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts" + }, { "plugin": "elasticAssistant", - "path": "x-pack/plugins/elastic_assistant/server/routes/helpers.ts" + "path": "x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/create_knowledge_base_entry.ts" }, { "plugin": "elasticAssistant", @@ -730,6 +738,10 @@ "plugin": "elasticAssistant", "path": "x-pack/plugins/elastic_assistant/server/routes/attack_discovery/post/helpers/handle_graph_error/index.tsx" }, + { + "plugin": "elasticAssistant", + "path": "x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts" + }, { "plugin": "globalSearchBar", "path": "x-pack/plugins/global_search_bar/public/telemetry/event_reporter.ts" @@ -804,151 +816,7 @@ }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_service.ts" }, { "plugin": "osquery", @@ -1178,6 +1046,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.test.ts" }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts" + }, + { + "plugin": "@kbn/langchain", + "path": "x-pack/packages/kbn-langchain/server/tracers/telemetry/telemetry_tracer.test.ts" + }, { "plugin": "@kbn/shared-ux-chrome-navigation", "path": "packages/shared-ux/chrome/navigation/mocks/storybook.ts" diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 027b64516dd2..ed1a968c83f5 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index ca5267187594..a439349d60a6 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 528184e40fe6..04e2124bb9c9 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 5544997621fc..c5bc8ea96ebb 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 89b7c2b63cf0..8ca3a84902b6 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index e44459ba92cc..b092910766ce 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index a59e0fd0122e..8fbc068a4706 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index d3facda9ccec..437494990ec6 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index ae9b561db4ab..8360e30d6890 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index c777532f6b07..cad169815e48 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index c7c42f06e30c..74026cbd53e9 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 43e7c3e5513f..0240046659de 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index f19a27549639..dd4240e077be 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 586c4f9c345c..672f23c9f90d 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 4ee4c2f5c8e4..c785a618c178 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index dfa28b6a22f8..746a4b1c7663 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 66420c559645..aee86b190440 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 6e19e15f1a23..7cf4a32d639c 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.devdocs.json b/api_docs/kbn_core_chrome_browser.devdocs.json index 33a27d209c1a..3c3be73393fd 100644 --- a/api_docs/kbn_core_chrome_browser.devdocs.json +++ b/api_docs/kbn_core_chrome_browser.devdocs.json @@ -3765,7 +3765,7 @@ "label": "AppDeepLinkId", "description": [], "signature": [ - "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" + "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"elasticsearchIndices:createIndex\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" ], "path": "packages/core/chrome/core-chrome-browser/src/project_navigation.ts", "deprecated": false, diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index fae17c4d2021..8032ac640544 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index ab7f74db2545..75e2407cd10a 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index c34b4e1f533c..a433a292166d 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 009e5c702f75..c55f6aebf24e 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index db761838cf7f..264a15c21f09 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 756e0a0cba8c..bc0608681fd2 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 03329ecd441e..d737e6e6ca44 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 27a840cef5d7..6ac8945bd5ed 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 843b9169cf8e..c1b509be36d7 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 88b5997a4315..58a80bf9bfa8 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 95ef79887c8c..2ae00993441d 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 54b8e9c5878d..9242b3fdfeab 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 5828e493045e..0bf08b893122 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index db5e8657b9d2..1f739c5487f5 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 1f5ad1c886ae..a5cccdd0d5a0 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index b663a8930a22..551be6ea0218 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index b72b34521f00..9a65ae1e2c5b 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index d3b56e55ecbd..4de0ca0c9cfb 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 1689c0c67462..80b3e3e26641 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 9ba038c0cbd3..b882e55e2868 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index fe3e04f5f54c..3c8ea9e8c466 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 7dc5ad0280df..84df80a37f93 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 551889a02bcb..f50ca818415f 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index a47779bc7c15..c5826d203475 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index d9af01ee7a93..2f73e097f7c9 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 576eb544e002..fa7781332cfa 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index cb7556a01493..4c36b5581b46 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index a166c8836108..a0e64301bb79 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 08a53fcefcfa..6c9755570d2d 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 54ea0083dbbd..127df174a3bc 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 7721d2217fd6..30d224c19668 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 14e70ec33c1e..b9c20af8150d 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index d4030babd0cb..d47ddada7166 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 038f106ae49f..6ee19e86b582 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 7ce2e42f0f3a..310c9bf7bd3e 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 267ab94c4500..679920d6b3db 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index c16e7f40af97..3e0bfcaa8304 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser.mdx b/api_docs/kbn_core_feature_flags_browser.mdx index 78c5943840e6..793b33b79ab0 100644 --- a/api_docs/kbn_core_feature_flags_browser.mdx +++ b/api_docs/kbn_core_feature_flags_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser title: "@kbn/core-feature-flags-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser'] --- import kbnCoreFeatureFlagsBrowserObj from './kbn_core_feature_flags_browser.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_internal.mdx b/api_docs/kbn_core_feature_flags_browser_internal.mdx index 85d2546f097a..db78d3fe60e6 100644 --- a/api_docs/kbn_core_feature_flags_browser_internal.mdx +++ b/api_docs/kbn_core_feature_flags_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-internal title: "@kbn/core-feature-flags-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-internal'] --- import kbnCoreFeatureFlagsBrowserInternalObj from './kbn_core_feature_flags_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_mocks.mdx b/api_docs/kbn_core_feature_flags_browser_mocks.mdx index cba51e77d553..7d1fa5abe768 100644 --- a/api_docs/kbn_core_feature_flags_browser_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-mocks title: "@kbn/core-feature-flags-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-mocks'] --- import kbnCoreFeatureFlagsBrowserMocksObj from './kbn_core_feature_flags_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server.mdx b/api_docs/kbn_core_feature_flags_server.mdx index 698de8981409..bbde0e51faad 100644 --- a/api_docs/kbn_core_feature_flags_server.mdx +++ b/api_docs/kbn_core_feature_flags_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server title: "@kbn/core-feature-flags-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server'] --- import kbnCoreFeatureFlagsServerObj from './kbn_core_feature_flags_server.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_internal.mdx b/api_docs/kbn_core_feature_flags_server_internal.mdx index 24db35fafbf8..a8258021ff78 100644 --- a/api_docs/kbn_core_feature_flags_server_internal.mdx +++ b/api_docs/kbn_core_feature_flags_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-internal title: "@kbn/core-feature-flags-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-internal'] --- import kbnCoreFeatureFlagsServerInternalObj from './kbn_core_feature_flags_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_mocks.mdx b/api_docs/kbn_core_feature_flags_server_mocks.mdx index 841cf097df01..e91372be6686 100644 --- a/api_docs/kbn_core_feature_flags_server_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-mocks title: "@kbn/core-feature-flags-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-mocks'] --- import kbnCoreFeatureFlagsServerMocksObj from './kbn_core_feature_flags_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index dcfe391a99df..db7d25304478 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 00137d31416f..10cbd80fa71e 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 97710cf69f5d..9e0719fd6226 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 41529e7adb13..f0cbb937bd0f 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 89ae18137a86..22a6fb73a4ae 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index cf823ed9de48..555a50af31ce 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index d21471012ce0..a5a3777f62ce 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 57d2294508c0..8a68b7151014 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index a1f7b520243e..1e3c7b50cb54 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 806e97209a17..02094a11da01 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 7dea6b9b51af..4e72693fbcae 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 8be8df4eef11..8211b20d63c7 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -4881,6 +4881,10 @@ "plugin": "stackConnectors", "path": "x-pack/plugins/stack_connectors/server/routes/get_well_known_email_service.ts" }, + { + "plugin": "stackConnectors", + "path": "x-pack/plugins/stack_connectors/server/routes/get_inference_services.ts" + }, { "plugin": "upgradeAssistant", "path": "x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts" @@ -5309,6 +5313,10 @@ "plugin": "ruleRegistry", "path": "x-pack/plugins/rule_registry/server/routes/__mocks__/server.ts" }, + { + "plugin": "stackConnectors", + "path": "x-pack/plugins/stack_connectors/server/routes/get_inference_services.test.ts" + }, { "plugin": "stackConnectors", "path": "x-pack/plugins/stack_connectors/server/routes/get_well_known_email_service.test.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 22a55999b2b2..c383d3d6e196 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 76a3b0ffeb78..83ee885d5b20 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 7e847684aadc..a4b726599b13 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 06174be7b93d..05cc2952a4af 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index b5de2f82119b..f1b51bcfa87a 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 1977c5e0a8be..a65144f8ba30 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index eee3c8da40f3..adc74ef1f06d 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 894797810843..11894e83ad03 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 98f27e65cdc2..55bc934440b3 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 034c9ad3f48c..54c00e630fb7 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index a4f0dddf96a5..63f3d37559be 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index a0046c0797b3..40bd1317d3c2 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index f5fd4db35139..b4ea8da0ef01 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index cc0d5956cd1f..f3538ffa4dac 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 8aa59648d079..2c2a6c5b177e 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 90380bb9c42a..b45253373509 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 3fc4d0fc36de..1c3a7d408a4b 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 1d68b54f686a..834f1f36f612 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 5590e08233ca..831056b508fc 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index da22edb8269c..5d992ebf8c7e 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 7f768178d564..288efd553f05 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index f411d12c497b..236a8d2d7dde 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 0bc94373c6df..dcbecaf76180 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 6dfbeac99e48..594b3db1ef9d 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 2f12d246852d..fa8a2e37ef0e 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index b51ef6731a04..4d73bf4d98a2 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 412506323d4d..012eda39becc 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 696aedd38ebf..c7faa587a4df 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index a9f10d3f054b..0a0c46759a1e 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index abc649756aba..50e0d2ca2711 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 5db989cb9a02..a9b42a446f33 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index da92f53770b4..037504f5764f 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index cd1254b8b4de..29787d92e125 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index d84d540f812a..47f2713f6ce2 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 8fad40262e6a..9a01acebf022 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index cc93c2408a19..ce9178e7537a 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 9d4d3224e691..33bd5fa63878 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index 91b3a204785d..7f60209675cd 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 828516d93884..56baf81f1aa1 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index b0ca1147804b..508a9fdbbe39 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index a7c367e63cd8..957f6cb6ae9f 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 00979968dbfc..6cb81ba1b176 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 3cf0037d7aa8..e07fdc43eddc 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 2e226e422d9d..e03db8dd11d7 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index b1ddfdbb2b39..e0db4f9df452 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index da9c9329c82c..2f9512345141 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index f6f429c22e45..bd5a474c975d 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index f39127097422..fd05514ecf0d 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 553e6ff18034..4e4f95947b23 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 519c91ca3ec6..5b4b09e5eb4b 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 456c43b2938f..3789f0ffd14b 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 8b01f07d1f64..9f175b48961b 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 7419aa886564..57993e1bf606 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index ad9815f9ab5f..f49475ec5aae 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 4cf04f0b1cb8..7de2c4c3eabe 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index 122e9c5027e8..911c0b4661aa 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index c4b6619397ac..65584cde18ee 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 9b7ceeb03164..7cb1ffe66031 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index eed8fc80a82c..6cbf30d3ea64 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 7d9631a9a7ab..805dbd672bd2 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 8430dd99548b..265339129195 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index f24bb0ea72ea..f842bc5c03f2 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 668c2b281640..483ac1602b82 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index ff60605a55cf..403a2a37c289 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 69c3b72d20e4..0dcb447e8fee 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index bc60bd1cb57c..e55d56082469 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 4ecf160b14fa..fd8915f08c0c 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index e1a555c9898f..e8d59c0a1a20 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index 177ab26c9f6d..44f101b0964f 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 95285462ec83..5c61fb024f1b 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index d3e9d7ee423f..10fb8ff1e098 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index dd50142759b7..5ae33f158714 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index d23544e68946..cef994182b97 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index c353565cdc9e..b2b2cb7b568f 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index c109afd8f623..ef527c950cd3 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 2bf3636a02e8..938f83007fbc 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index bb35ab5dbf02..a50c3b1828d6 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 6f11e379373e..6a4b625285f0 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index e741bb5e7e4c..02c5352bd4d1 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index b207e9f64712..8aaf6a5f2adf 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index b0cdeaf2f00e..6aa9349a0352 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 7e279df6cc60..87b2f6d5c939 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index fc6ad7b2c886..32ecd45349e1 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 6e0ff959d745..4cbeda7788db 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index 1fb29c6c899a..d953b935075e 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index fa2bce7f6cbd..fff46b5326d0 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index e2b7e33a64ec..53cf35bd07a4 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index a0369dfa5c1c..d038ab88a60a 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 0839550d97f6..b3a3706f5914 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 3de68be7936f..a2556a776c14 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 48dec3594a8e..f5e222efbd2b 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index cd16b7558abf..a4f5b11d8bdb 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 845e1f0f224d..54002865f12d 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 1500811c5906..9e0390f9284c 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 50570ba05398..c46f4f10e0e5 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 86d77d447dd1..f6e6a8b57420 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index cb9207ae2bc8..9d342ea1d869 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index bc81d9a28fd4..c86d118151f0 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index bc877942ccac..4e32a6961089 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 720bc1b93fe6..b2e719a02d6b 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 6aa24c02eded..44c3d2ffb345 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 5008643b50f5..88c82c120c85 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index d90dde244a34..85949052d0c4 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 380f53922e17..46e178ce0b71 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index bd37988bcb37..f118affa95cd 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 33f742426f3e..e663abed4b7e 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index ca1d92e803ba..c5bcf7da108b 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 68d52f798093..05a5dbabe2df 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index 884c096c6f19..a8388fdda54c 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 06dbe09dee10..ab42e4e76223 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 16e9c878c437..d4e94e43d07e 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index be2e38e98ea8..44350ffee001 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index a7fd20e6ed96..53b25850f8df 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 83c60338d0c7..9af32e7352b2 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index e20555dbab9a..7190eea79989 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 0986c2ee9c48..9b6d2bef5fca 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index f67b1d1beda2..64a284f83f05 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index a77d9fcc9ade..46c0550d1e33 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 376e5e492d04..7dfd297037c5 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.devdocs.json b/api_docs/kbn_deeplinks_search.devdocs.json index d8cd4e993a4e..22c6f9258754 100644 --- a/api_docs/kbn_deeplinks_search.devdocs.json +++ b/api_docs/kbn_deeplinks_search.devdocs.json @@ -30,7 +30,7 @@ "label": "DeepLinkId", "description": [], "signature": [ - "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\"" + "\"appSearch\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"elasticsearchIndices:createIndex\"" ], "path": "packages/deeplinks/search/deep_links.ts", "deprecated": false, diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 5f20b9959df1..595d9fe34f8d 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 06c29487c0ac..c8972591f99c 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index d4a64d6bb2a2..3aaefdeed692 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index cb8fcf5ec07f..3ec8e4ffe178 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 18eecf2d4bcc..938aac32af48 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 1135b89985cb..6a1a579bcc4e 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index c1ddebf012dd..d68d42a1fbc3 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 893a968e13a5..864f684cd5d6 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index d116d9639ea1..2f83a9993ece 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index a5a03e485dad..dbf2659bfd26 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 126248514b12..99b18514c257 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_contextual_components.devdocs.json b/api_docs/kbn_discover_contextual_components.devdocs.json index b987fc426742..cd6c2ab526e2 100644 --- a/api_docs/kbn_discover_contextual_components.devdocs.json +++ b/api_docs/kbn_discover_contextual_components.devdocs.json @@ -174,9 +174,9 @@ "ExplainExplanation", " | undefined; highlight?: Record | undefined; inner_hits?: Record | undefined; matched_queries?: string[] | undefined; _nested?: ", + "> | undefined; matched_queries?: string[] | Record | undefined; _nested?: ", "SearchNestedIdentity", - " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; }; id: string; isAnchor?: boolean | undefined; }" + " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; }; id: string; isAnchor?: boolean | undefined; }" ], "path": "packages/kbn-discover-contextual-components/src/data_types/logs/components/summary_column/utils.tsx", "deprecated": false, diff --git a/api_docs/kbn_discover_contextual_components.mdx b/api_docs/kbn_discover_contextual_components.mdx index f90b6f898563..68e6317d384e 100644 --- a/api_docs/kbn_discover_contextual_components.mdx +++ b/api_docs/kbn_discover_contextual_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-contextual-components title: "@kbn/discover-contextual-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-contextual-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-contextual-components'] --- import kbnDiscoverContextualComponentsObj from './kbn_discover_contextual_components.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index bf4f841400e2..ec4e0bd3901b 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 850c4a18ee39..0ef234ad26dc 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 2702f6915693..100e0358119e 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 089448654b15..50724896d794 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index f31719fe91b9..60105e01565f 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 996db7dec146..2a958b1dc9ac 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index dda2e2030e56..b9658cbf5597 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index ef12d33ed1a7..81b029f25b45 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index a21a8df0dfa5..1bd5f4dc7852 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index 21636d884944..a1cae02d138d 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index ebd77a91caaa..167bbfa298fd 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index e408bd32ffd3..2df4b2919483 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index a0cbfffe7bc9..8cba7dad7d05 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index b4037d794893..7e3765672afd 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.devdocs.json b/api_docs/kbn_es_types.devdocs.json index d9baac147e26..60ef30c14df1 100644 --- a/api_docs/kbn_es_types.devdocs.json +++ b/api_docs/kbn_es_types.devdocs.json @@ -493,6 +493,8 @@ "AggregationsRateAggregation", "; reverse_nested: ", "AggregationsReverseNestedAggregation", + "; random_sampler: ", + "AggregationsRandomSamplerAggregation", "; sampler: ", "AggregationsSamplerAggregation", "; scripted_metric: ", @@ -515,6 +517,8 @@ "AggregationsSumBucketAggregation", "; terms: ", "AggregationsTermsAggregation", + "; time_series: ", + "AggregationsTimeSeriesAggregation", "; top_hits: ", "AggregationsTopHitsAggregation", "; t_test: ", diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 010294f3687c..3008d0904925 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 2ca9fb754a86..9ad9028b3f58 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 868d111d0832..c1ec12593685 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_editor.mdx b/api_docs/kbn_esql_editor.mdx index d93d65c75fbe..0623f4544296 100644 --- a/api_docs/kbn_esql_editor.mdx +++ b/api_docs/kbn_esql_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-editor title: "@kbn/esql-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-editor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-editor'] --- import kbnEsqlEditorObj from './kbn_esql_editor.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 7eb695f3eb09..606cd098419e 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 9cb6536735f1..bdda8ed94622 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index cf150d858494..71ac2174e691 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index a6b94398890c..ee224c8deeed 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 489e50b25ec9..5431633de245 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 55f5c1ba26bc..69e7372f4de5 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 59965cf55036..a69e34702878 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index aaa171be0487..dc8f6b7f3be1 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index d0cd317591a2..36d81b43db66 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 7ca5d78746af..a867f15a2ef9 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index d7acef3d9f42..723193f3ff8c 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 1113833946ce..431e1754c3d8 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 345cbfb15640..735ec74e591c 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index dda4c0f14cc8..993d1c37ed4f 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grid_layout.mdx b/api_docs/kbn_grid_layout.mdx index ced780a7de48..57a3074656dc 100644 --- a/api_docs/kbn_grid_layout.mdx +++ b/api_docs/kbn_grid_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grid-layout title: "@kbn/grid-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grid-layout plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grid-layout'] --- import kbnGridLayoutObj from './kbn_grid_layout.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index c734b583edd7..95cce0d41bab 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 2d69201e6a3e..567731d8779b 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 75f1a04ce809..2124fcf4aed6 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index dbbd634346bc..320bed94d54a 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index cfc60f348ca7..1d262d68de22 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 0dbf15092c99..a706989eea5a 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 19368ad58dea..26fe80f6e1c7 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 94eb34e6c284..f4ce63cdc439 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index b771ec600f31..8b7f563d8da9 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 2a131f248499..c776e26e543b 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_lifecycle_management_common_shared.devdocs.json b/api_docs/kbn_index_lifecycle_management_common_shared.devdocs.json new file mode 100644 index 000000000000..62664293d6a4 --- /dev/null +++ b/api_docs/kbn_index_lifecycle_management_common_shared.devdocs.json @@ -0,0 +1,1447 @@ +{ + "id": "@kbn/index-lifecycle-management-common-shared", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.AllocateAction", + "type": "Interface", + "tags": [], + "label": "AllocateAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.AllocateAction.number_of_replicas", + "type": "number", + "tags": [], + "label": "number_of_replicas", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.AllocateAction.include", + "type": "Object", + "tags": [], + "label": "include", + "description": [], + "signature": [ + "{} | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.AllocateAction.exclude", + "type": "Object", + "tags": [], + "label": "exclude", + "description": [], + "signature": [ + "{} | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.AllocateAction.require", + "type": "Object", + "tags": [], + "label": "require", + "description": [], + "signature": [ + "{ [attribute: string]: string; } | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.CommonPhaseSettings", + "type": "Interface", + "tags": [], + "label": "CommonPhaseSettings", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.CommonPhaseSettings.phaseEnabled", + "type": "boolean", + "tags": [], + "label": "phaseEnabled", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.DeletePhase", + "type": "Interface", + "tags": [], + "label": "DeletePhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.DeletePhase", + "text": "DeletePhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.CommonPhaseSettings", + "text": "CommonPhaseSettings" + }, + ",", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.PhaseWithMinAge", + "text": "PhaseWithMinAge" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.DeletePhase.waitForSnapshotPolicy", + "type": "string", + "tags": [], + "label": "waitForSnapshotPolicy", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.DownsampleAction", + "type": "Interface", + "tags": [], + "label": "DownsampleAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.DownsampleAction.fixed_interval", + "type": "string", + "tags": [], + "label": "fixed_interval", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ForcemergeAction", + "type": "Interface", + "tags": [], + "label": "ForcemergeAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ForcemergeAction.max_num_segments", + "type": "number", + "tags": [], + "label": "max_num_segments", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ForcemergeAction.index_codec", + "type": "string", + "tags": [], + "label": "index_codec", + "description": [], + "signature": [ + "\"best_compression\" | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.MigrateAction", + "type": "Interface", + "tags": [], + "label": "MigrateAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.MigrateAction.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [ + "\nIf enabled is ever set it will probably only be set to `false` because the default value\nfor this is `true`. Rather leave unspecified for true when serialising." + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases", + "type": "Interface", + "tags": [], + "label": "Phases", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases.hot", + "type": "Object", + "tags": [], + "label": "hot", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedHotPhase", + "text": "SerializedHotPhase" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases.warm", + "type": "Object", + "tags": [], + "label": "warm", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedWarmPhase", + "text": "SerializedWarmPhase" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases.cold", + "type": "Object", + "tags": [], + "label": "cold", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedColdPhase", + "text": "SerializedColdPhase" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases.frozen", + "type": "Object", + "tags": [], + "label": "frozen", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedFrozenPhase", + "text": "SerializedFrozenPhase" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phases.delete", + "type": "Object", + "tags": [], + "label": "delete", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedDeletePhase", + "text": "SerializedDeletePhase" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithMinAge", + "type": "Interface", + "tags": [], + "label": "PhaseWithMinAge", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithMinAge.selectedMinimumAge", + "type": "string", + "tags": [], + "label": "selectedMinimumAge", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithMinAge.selectedMinimumAgeUnits", + "type": "string", + "tags": [], + "label": "selectedMinimumAgeUnits", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES", + "type": "Interface", + "tags": [], + "label": "PolicyFromES", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.modifiedDate", + "type": "string", + "tags": [], + "label": "modifiedDate", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.policy", + "type": "Object", + "tags": [], + "label": "policy", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPolicy", + "text": "SerializedPolicy" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.version", + "type": "number", + "tags": [], + "label": "version", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.indices", + "type": "Array", + "tags": [], + "label": "indices", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.dataStreams", + "type": "Array", + "tags": [], + "label": "dataStreams", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PolicyFromES.indexTemplates", + "type": "Array", + "tags": [], + "label": "indexTemplates", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction", + "type": "Interface", + "tags": [], + "label": "RolloverAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction.max_age", + "type": "string", + "tags": [], + "label": "max_age", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction.max_docs", + "type": "number", + "tags": [], + "label": "max_docs", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction.max_primary_shard_size", + "type": "string", + "tags": [], + "label": "max_primary_shard_size", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction.max_primary_shard_docs", + "type": "number", + "tags": [], + "label": "max_primary_shard_docs", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.RolloverAction.max_size", + "type": "string", + "tags": [ + "deprecated" + ], + "label": "max_size", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": true, + "trackAdoption": false, + "references": [ + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/__jest__/mocks.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/serializer/serializer.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/policy_flyout/components/rollover.tsx" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/policy_flyout/components/rollover.tsx" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/integration_tests/edit_policy/constants.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts" + }, + { + "plugin": "indexLifecycleManagement", + "path": "x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/form/deserializer_and_serializer.test.ts" + } + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SearchableSnapshotAction", + "type": "Interface", + "tags": [], + "label": "SearchableSnapshotAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SearchableSnapshotAction.snapshot_repository", + "type": "string", + "tags": [], + "label": "snapshot_repository", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SearchableSnapshotAction.force_merge_index", + "type": "CompoundType", + "tags": [], + "label": "force_merge_index", + "description": [ + "\nWe do not configure this value in the UI as it is an advanced setting that will\nnot suit the vast majority of cases." + ], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedActionWithAllocation", + "type": "Interface", + "tags": [], + "label": "SerializedActionWithAllocation", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedActionWithAllocation.allocate", + "type": "Object", + "tags": [], + "label": "allocate", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.AllocateAction", + "text": "AllocateAction" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedActionWithAllocation.migrate", + "type": "Object", + "tags": [], + "label": "migrate", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.MigrateAction", + "text": "MigrateAction" + }, + " | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedColdPhase", + "type": "Interface", + "tags": [], + "label": "SerializedColdPhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedColdPhase", + "text": "SerializedColdPhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPhase", + "text": "SerializedPhase" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedColdPhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ freeze?: {} | undefined; readonly?: {} | undefined; downsample?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.DownsampleAction", + "text": "DownsampleAction" + }, + " | undefined; allocate?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.AllocateAction", + "text": "AllocateAction" + }, + " | undefined; set_priority?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SetPriorityAction", + "text": "SetPriorityAction" + }, + " | undefined; migrate?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.MigrateAction", + "text": "MigrateAction" + }, + " | undefined; searchable_snapshot?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SearchableSnapshotAction", + "text": "SearchableSnapshotAction" + }, + " | undefined; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedDeletePhase", + "type": "Interface", + "tags": [], + "label": "SerializedDeletePhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedDeletePhase", + "text": "SerializedDeletePhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPhase", + "text": "SerializedPhase" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedDeletePhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ wait_for_snapshot?: { policy: string; } | undefined; delete?: { delete_searchable_snapshot?: boolean | undefined; } | undefined; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedFrozenPhase", + "type": "Interface", + "tags": [], + "label": "SerializedFrozenPhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedFrozenPhase", + "text": "SerializedFrozenPhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPhase", + "text": "SerializedPhase" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedFrozenPhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ searchable_snapshot?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SearchableSnapshotAction", + "text": "SearchableSnapshotAction" + }, + " | undefined; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedHotPhase", + "type": "Interface", + "tags": [], + "label": "SerializedHotPhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedHotPhase", + "text": "SerializedHotPhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPhase", + "text": "SerializedPhase" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedHotPhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ rollover?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.RolloverAction", + "text": "RolloverAction" + }, + " | undefined; forcemerge?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.ForcemergeAction", + "text": "ForcemergeAction" + }, + " | undefined; readonly?: {} | undefined; shrink?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.ShrinkAction", + "text": "ShrinkAction" + }, + " | undefined; downsample?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.DownsampleAction", + "text": "DownsampleAction" + }, + " | undefined; set_priority?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SetPriorityAction", + "text": "SetPriorityAction" + }, + " | undefined; searchable_snapshot?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SearchableSnapshotAction", + "text": "SearchableSnapshotAction" + }, + " | undefined; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPhase", + "type": "Interface", + "tags": [], + "label": "SerializedPhase", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPhase.min_age", + "type": "string", + "tags": [], + "label": "min_age", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ [action: string]: any; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPolicy", + "type": "Interface", + "tags": [], + "label": "SerializedPolicy", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPolicy.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPolicy.phases", + "type": "Object", + "tags": [], + "label": "phases", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.Phases", + "text": "Phases" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPolicy.deprecated", + "type": "CompoundType", + "tags": [], + "label": "deprecated", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedPolicy._meta", + "type": "Object", + "tags": [], + "label": "_meta", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedWarmPhase", + "type": "Interface", + "tags": [], + "label": "SerializedWarmPhase", + "description": [], + "signature": [ + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedWarmPhase", + "text": "SerializedWarmPhase" + }, + " extends ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SerializedPhase", + "text": "SerializedPhase" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SerializedWarmPhase.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + "{ allocate?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.AllocateAction", + "text": "AllocateAction" + }, + " | undefined; shrink?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.ShrinkAction", + "text": "ShrinkAction" + }, + " | undefined; forcemerge?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.ForcemergeAction", + "text": "ForcemergeAction" + }, + " | undefined; readonly?: {} | undefined; downsample?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.DownsampleAction", + "text": "DownsampleAction" + }, + " | undefined; set_priority?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.SetPriorityAction", + "text": "SetPriorityAction" + }, + " | undefined; migrate?: ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.MigrateAction", + "text": "MigrateAction" + }, + " | undefined; }" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SetPriorityAction", + "type": "Interface", + "tags": [], + "label": "SetPriorityAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.SetPriorityAction.priority", + "type": "CompoundType", + "tags": [], + "label": "priority", + "description": [], + "signature": [ + "number | null" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ShrinkAction", + "type": "Interface", + "tags": [], + "label": "ShrinkAction", + "description": [], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ShrinkAction.number_of_shards", + "type": "number", + "tags": [], + "label": "number_of_shards", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ShrinkAction.max_primary_shard_size", + "type": "string", + "tags": [], + "label": "max_primary_shard_size", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ShrinkAction.allow_write_after_shrink", + "type": "CompoundType", + "tags": [], + "label": "allow_write_after_shrink", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.ILM_LOCATOR_ID", + "type": "string", + "tags": [], + "label": "ILM_LOCATOR_ID", + "description": [], + "signature": [ + "\"ILM_LOCATOR_ID\"" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.Phase", + "type": "Type", + "tags": [], + "label": "Phase", + "description": [], + "signature": [ + "keyof ", + { + "pluginId": "@kbn/index-lifecycle-management-common-shared", + "scope": "common", + "docId": "kibKbnIndexLifecycleManagementCommonSharedPluginApi", + "section": "def-common.Phases", + "text": "Phases" + } + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseExceptDelete", + "type": "Type", + "tags": [], + "label": "PhaseExceptDelete", + "description": [], + "signature": [ + "\"warm\" | \"hot\" | \"frozen\" | \"cold\"" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithAllocation", + "type": "Type", + "tags": [], + "label": "PhaseWithAllocation", + "description": [], + "signature": [ + "\"warm\" | \"cold\"" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithDownsample", + "type": "Type", + "tags": [], + "label": "PhaseWithDownsample", + "description": [], + "signature": [ + "\"warm\" | \"hot\" | \"cold\"" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/index-lifecycle-management-common-shared", + "id": "def-common.PhaseWithTiming", + "type": "Type", + "tags": [], + "label": "PhaseWithTiming", + "description": [], + "signature": [ + "\"delete\" | \"warm\" | \"frozen\" | \"cold\"" + ], + "path": "x-pack/packages/index-lifecycle-management/index_lifecycle_management_common_shared/src/policies.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_index_lifecycle_management_common_shared.mdx b/api_docs/kbn_index_lifecycle_management_common_shared.mdx new file mode 100644 index 000000000000..0ed700c24a6e --- /dev/null +++ b/api_docs/kbn_index_lifecycle_management_common_shared.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnIndexLifecycleManagementCommonSharedPluginApi +slug: /kibana-dev-docs/api/kbn-index-lifecycle-management-common-shared +title: "@kbn/index-lifecycle-management-common-shared" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/index-lifecycle-management-common-shared plugin +date: 2024-11-12 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-lifecycle-management-common-shared'] +--- +import kbnIndexLifecycleManagementCommonSharedObj from './kbn_index_lifecycle_management_common_shared.devdocs.json'; + + + +Contact [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 75 | 0 | 73 | 0 | + +## Common + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_index_management_shared_types.mdx b/api_docs/kbn_index_management_shared_types.mdx index 9aa8c3d4a852..8c28979c41e4 100644 --- a/api_docs/kbn_index_management_shared_types.mdx +++ b/api_docs/kbn_index_management_shared_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management-shared-types title: "@kbn/index-management-shared-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management-shared-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management-shared-types'] --- import kbnIndexManagementSharedTypesObj from './kbn_index_management_shared_types.devdocs.json'; diff --git a/api_docs/kbn_inference_common.mdx b/api_docs/kbn_inference_common.mdx index 5707b0dee844..8724d9451e1d 100644 --- a/api_docs/kbn_inference_common.mdx +++ b/api_docs/kbn_inference_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference-common title: "@kbn/inference-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference-common'] --- import kbnInferenceCommonObj from './kbn_inference_common.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 13f101fde9b1..a8e658592606 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 4c34cfd00104..ff578001a59f 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 29c5511b68be..7a1f6e64c175 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_investigation_shared.mdx b/api_docs/kbn_investigation_shared.mdx index 2fa7a1803456..a1dd7ecaaf2b 100644 --- a/api_docs/kbn_investigation_shared.mdx +++ b/api_docs/kbn_investigation_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-investigation-shared title: "@kbn/investigation-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/investigation-shared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/investigation-shared'] --- import kbnInvestigationSharedObj from './kbn_investigation_shared.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 3b613b0693a9..a104f3097eff 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 3009daa0e2c8..2b496ce99a8a 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_item_buffer.mdx b/api_docs/kbn_item_buffer.mdx index 1b3675bc8b84..38d654ba2bb8 100644 --- a/api_docs/kbn_item_buffer.mdx +++ b/api_docs/kbn_item_buffer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-item-buffer title: "@kbn/item-buffer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/item-buffer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/item-buffer'] --- import kbnItemBufferObj from './kbn_item_buffer.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index a1cdf973c4c2..c2fec834962d 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index a42099416fc0..d213ff07e87f 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index f380f815b234..3fe23883d8cb 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index 600f8bee812e..3a94e136d53e 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index dd2c54b529e6..9d4377358e12 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation.mdx b/api_docs/kbn_language_documentation.mdx index 92295b77760e..85764d5c9825 100644 --- a/api_docs/kbn_language_documentation.mdx +++ b/api_docs/kbn_language_documentation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation title: "@kbn/language-documentation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation'] --- import kbnLanguageDocumentationObj from './kbn_language_documentation.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 700581c1f4cc..535c118c9dec 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 95e4bc76d5fb..106924491873 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 5e4e4d829651..c3154b703fed 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index be0f183a8285..e6be28e24ce4 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index 96dda426347f..2e6ffa89d004 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index eeb5d66bd9c7..f152444c1f62 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index eb86ef98a7ca..69e712b46815 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 65e8bbcbc1c3..16e99fc5cc94 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 0d9916e3ee07..a00641e9efbc 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 6f8aeed4cc3f..b071cbc09693 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 82e828588984..f79d711ca521 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 618aca65de9d..de54101e88d0 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 9d566f9a6c0f..940d0042bfc8 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index 19accbeb0d25..bc654d267378 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -907,21 +907,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.METRICS_ALLOW_CHECKING_FOR_FAILED_SHARDS_ID", - "type": "string", - "tags": [], - "label": "METRICS_ALLOW_CHECKING_FOR_FAILED_SHARDS_ID", - "description": [], - "signature": [ - "\"metrics:allowCheckingForFailedShards\"" - ], - "path": "packages/kbn-management/settings/setting_ids/index.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/management-settings-ids", "id": "def-common.METRICS_ALLOW_STRING_INDICES_ID", diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 6b89f67b5a37..0a46cd757128 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 140 | 0 | 139 | 0 | +| 139 | 0 | 138 | 0 | ## Common diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 2ca4f14bea68..c4dba23025fd 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 6b7f0081a1f7..b52d09baa28e 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 3476906b1425..03d44b182880 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 41476ad4398e..a001d12638f0 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_manifest.mdx b/api_docs/kbn_manifest.mdx index a0eb170086db..8fd4aea6ac22 100644 --- a/api_docs/kbn_manifest.mdx +++ b/api_docs/kbn_manifest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-manifest title: "@kbn/manifest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/manifest plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/manifest'] --- import kbnManifestObj from './kbn_manifest.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 51cb11fcebb6..b0b5c6273ea9 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index abf3bda5f933..2cc52ca631c6 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 2348dac81da6..7dcf7843651c 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 45877286f45e..49bcc89d3d99 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index f87deb92a7ea..a4d9b5a984f3 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index b2c17e20cb38..a79a0aeaf53e 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 859294aec3e9..ff3a6ba6c48a 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index e3855f3b4423..4c3f4b54e464 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 8fe36e758371..fddb315a8919 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 54c5002e3eac..dbb7cc6c40cc 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index a36e4dda743f..7d6c510e3420 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 5c17c51d194e..14cfe80893ad 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_field_stats_flyout.mdx b/api_docs/kbn_ml_field_stats_flyout.mdx index 27e6e30a9dbd..7d5b991fac89 100644 --- a/api_docs/kbn_ml_field_stats_flyout.mdx +++ b/api_docs/kbn_ml_field_stats_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-field-stats-flyout title: "@kbn/ml-field-stats-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-field-stats-flyout plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-field-stats-flyout'] --- import kbnMlFieldStatsFlyoutObj from './kbn_ml_field_stats_flyout.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index fa41e6be5453..adf618a7bc18 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index e403884bf0fa..b9a5f9259d43 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 4d28ea44b7a7..88020badbad1 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 4676703ad7e3..db9949aeef20 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index c70ef91f797d..4986f6e22610 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index a063f835e52f..7bebcbd38965 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 115f6ce86b6f..ffc7decec804 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_parse_interval.mdx b/api_docs/kbn_ml_parse_interval.mdx index c4d670af3164..5c276242db36 100644 --- a/api_docs/kbn_ml_parse_interval.mdx +++ b/api_docs/kbn_ml_parse_interval.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-parse-interval title: "@kbn/ml-parse-interval" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-parse-interval plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-parse-interval'] --- import kbnMlParseIntervalObj from './kbn_ml_parse_interval.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index dce4ae2c1919..9a1bab0cc2bb 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 0a2654df0647..aafa96694cab 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 2ad95a7ce885..b07f25d73fce 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index c8412a0ff7d2..63076aa68f7e 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 65c696631c06..596110c69468 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index ca2251874675..77d3e0cdb27d 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index b623fde61441..60724e72677b 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index dc039d3c17ad..343401b698cf 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 047116e2ef62..6df0063365e7 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_ml_validators.mdx b/api_docs/kbn_ml_validators.mdx index c9b832bf5c5c..23e597e2f0f4 100644 --- a/api_docs/kbn_ml_validators.mdx +++ b/api_docs/kbn_ml_validators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-validators title: "@kbn/ml-validators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-validators plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-validators'] --- import kbnMlValidatorsObj from './kbn_ml_validators.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index a1b6a19e3d1f..742d202cbcee 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 8f6716ec252a..a96e583d94e9 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 2d9431bbfc37..1defebd09d22 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_object_versioning_utils.mdx b/api_docs/kbn_object_versioning_utils.mdx index 5aed21660e2c..8a81cb4f518a 100644 --- a/api_docs/kbn_object_versioning_utils.mdx +++ b/api_docs/kbn_object_versioning_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning-utils title: "@kbn/object-versioning-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning-utils'] --- import kbnObjectVersioningUtilsObj from './kbn_object_versioning_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 5fa338f1213f..24b2d0f27bbd 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_rule_utils.mdx b/api_docs/kbn_observability_alerting_rule_utils.mdx index 223b5a4d8625..c6067a685e93 100644 --- a/api_docs/kbn_observability_alerting_rule_utils.mdx +++ b/api_docs/kbn_observability_alerting_rule_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-rule-utils title: "@kbn/observability-alerting-rule-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-rule-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-rule-utils'] --- import kbnObservabilityAlertingRuleUtilsObj from './kbn_observability_alerting_rule_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 350c62a55128..5ad4d6d3f360 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 72ea156722bd..d8207d81dc26 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_observability_logs_overview.mdx b/api_docs/kbn_observability_logs_overview.mdx index 38990a186855..58a812061ea3 100644 --- a/api_docs/kbn_observability_logs_overview.mdx +++ b/api_docs/kbn_observability_logs_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-logs-overview title: "@kbn/observability-logs-overview" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-logs-overview plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-logs-overview'] --- import kbnObservabilityLogsOverviewObj from './kbn_observability_logs_overview.devdocs.json'; diff --git a/api_docs/kbn_observability_synthetics_test_data.mdx b/api_docs/kbn_observability_synthetics_test_data.mdx index b6dca99b5396..b10096eabf86 100644 --- a/api_docs/kbn_observability_synthetics_test_data.mdx +++ b/api_docs/kbn_observability_synthetics_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-synthetics-test-data title: "@kbn/observability-synthetics-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-synthetics-test-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-synthetics-test-data'] --- import kbnObservabilitySyntheticsTestDataObj from './kbn_observability_synthetics_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index ff2270f6e779..e24dad4ac4c3 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 0edcc4cae272..bdb4b573a9f8 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 360f7ff6bf65..928955cf42b7 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index c3add8441c5b..b973e2fc2870 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index d86d4a80de8a..b365a12c78ac 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index d31b70779d54..e762e0372171 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 9dda16b63cfc..3165245e2fb9 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 63dc2e06b8bf..0ad957cfc519 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 423477a41877..996544ca5019 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index b3d7eb05ce47..a338af0b3786 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 4549db49f007..35c491dd3aa3 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 781ed0c62157..ea63d73b7320 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_product_doc_artifact_builder.mdx b/api_docs/kbn_product_doc_artifact_builder.mdx index a68423c371d8..e737c606ba67 100644 --- a/api_docs/kbn_product_doc_artifact_builder.mdx +++ b/api_docs/kbn_product_doc_artifact_builder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-product-doc-artifact-builder title: "@kbn/product-doc-artifact-builder" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/product-doc-artifact-builder plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/product-doc-artifact-builder'] --- import kbnProductDocArtifactBuilderObj from './kbn_product_doc_artifact_builder.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index e1f113d52967..2461ac60f5b9 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 1ad575c0449e..278a1b53b11e 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index f0dd611a892b..6cc9939a14c1 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 730027b67498..d0a86fc48bda 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 8ca4d214864e..fafac45b43fa 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 8e1e56347556..268d249666ad 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 84a34d96fd9c..89408f64335a 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index fadebde22658..642c0f510d32 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 872075cc85af..6d69b3e1feb5 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 81c398d0953a..23d49b1d84a8 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx index 8b82861786b7..fb70861bf5c8 100644 --- a/api_docs/kbn_recently_accessed.mdx +++ b/api_docs/kbn_recently_accessed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-recently-accessed title: "@kbn/recently-accessed" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/recently-accessed plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] --- import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 1a9bc978e881..c3e6209fe0cb 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 6219e6423081..0a344992f367 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index fce761ca608d..d4593bb69ec1 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 7c92964419f5..4bc026939319 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index b7cbf6c07aa1..495a620eed08 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 79367332f738..fcdbcff99440 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index fd7d81062dd9..bd9f21e11dc5 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 6f0e90696c23..a733ea5344aa 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index bef27400e8f4..1511dd22eeb3 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 30ac0f42d5d8..bff1a6c5a298 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 1f3f42f3bc2c..d9948ebfb004 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index 7a9509c2ab7d..cc2af3e7afe0 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index c96ead9dc538..823e66dde25c 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index fc78ba5e5c05..adb3de1a0cdc 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index f1f62ca430ee..18dfc6adf3e4 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 44104bcc2296..d6fcf1550c50 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index 2f8f1dbc0cad..b22e6a15fe0e 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_response_ops_rule_params.mdx b/api_docs/kbn_response_ops_rule_params.mdx index f1c7ce801a53..4d2c8f2bf282 100644 --- a/api_docs/kbn_response_ops_rule_params.mdx +++ b/api_docs/kbn_response_ops_rule_params.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-rule-params title: "@kbn/response-ops-rule-params" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-rule-params plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-rule-params'] --- import kbnResponseOpsRuleParamsObj from './kbn_response_ops_rule_params.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 736828e0ee66..a3f6b6cc34cc 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index 60768cf39526..f5b6092d2557 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index d9abbbb1bcd8..fe7d8a3d6799 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 5c024727b284..db74246897a4 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 5fd7882fa443..b7e99dcb1c42 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index d77c4509bb9e..7fad45ddc266 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index b3ecca674cab..017149429425 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_screenshotting_server.mdx b/api_docs/kbn_screenshotting_server.mdx index 25d7ceed25db..61e0f8fbcff1 100644 --- a/api_docs/kbn_screenshotting_server.mdx +++ b/api_docs/kbn_screenshotting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-screenshotting-server title: "@kbn/screenshotting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/screenshotting-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/screenshotting-server'] --- import kbnScreenshottingServerObj from './kbn_screenshotting_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_components.mdx b/api_docs/kbn_search_api_keys_components.mdx index fa0b92053ae2..ed9420b3658c 100644 --- a/api_docs/kbn_search_api_keys_components.mdx +++ b/api_docs/kbn_search_api_keys_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-components title: "@kbn/search-api-keys-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-components'] --- import kbnSearchApiKeysComponentsObj from './kbn_search_api_keys_components.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_server.mdx b/api_docs/kbn_search_api_keys_server.mdx index e6ea55259b3f..79819d9fc6fc 100644 --- a/api_docs/kbn_search_api_keys_server.mdx +++ b/api_docs/kbn_search_api_keys_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-server title: "@kbn/search-api-keys-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-server'] --- import kbnSearchApiKeysServerObj from './kbn_search_api_keys_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 337f147e3141..c5f4e270e01d 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index f49adeadd5fb..1acdad333882 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 99cae73c4bc8..5c32759e084f 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 824741e454bd..1c2ca8ebe121 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index c17d4290a9e6..b783e3a2eaf0 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_shared_ui.mdx b/api_docs/kbn_search_shared_ui.mdx index 0baa230c3863..0b2b651652dc 100644 --- a/api_docs/kbn_search_shared_ui.mdx +++ b/api_docs/kbn_search_shared_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-shared-ui title: "@kbn/search-shared-ui" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-shared-ui plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-shared-ui'] --- import kbnSearchSharedUiObj from './kbn_search_shared_ui.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 2621f48057e0..248eb3a1df12 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index a7488482ed48..dbb86b136acb 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core.mdx b/api_docs/kbn_security_authorization_core.mdx index 4567131c545a..20e1cbf0e18a 100644 --- a/api_docs/kbn_security_authorization_core.mdx +++ b/api_docs/kbn_security_authorization_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core title: "@kbn/security-authorization-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core'] --- import kbnSecurityAuthorizationCoreObj from './kbn_security_authorization_core.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core_common.mdx b/api_docs/kbn_security_authorization_core_common.mdx index e67ef3ac5326..fe630c194a15 100644 --- a/api_docs/kbn_security_authorization_core_common.mdx +++ b/api_docs/kbn_security_authorization_core_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core-common title: "@kbn/security-authorization-core-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core-common'] --- import kbnSecurityAuthorizationCoreCommonObj from './kbn_security_authorization_core_common.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index ca66db16c759..0ed1baaaded6 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 3d67c31c267f..84392a192248 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index 2c2a8b853318..fd13c63ea575 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index 122b4b8029a0..e7fdb4d05fd5 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index 80da55ce1761..206a5972bd9d 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_role_management_model.mdx b/api_docs/kbn_security_role_management_model.mdx index 2109360e0cdf..0a310c07fb5d 100644 --- a/api_docs/kbn_security_role_management_model.mdx +++ b/api_docs/kbn_security_role_management_model.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-role-management-model title: "@kbn/security-role-management-model" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-role-management-model plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-role-management-model'] --- import kbnSecurityRoleManagementModelObj from './kbn_security_role_management_model.devdocs.json'; diff --git a/api_docs/kbn_security_solution_distribution_bar.mdx b/api_docs/kbn_security_solution_distribution_bar.mdx index 88b8ec1de8d7..ed1ff48e2f75 100644 --- a/api_docs/kbn_security_solution_distribution_bar.mdx +++ b/api_docs/kbn_security_solution_distribution_bar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-distribution-bar title: "@kbn/security-solution-distribution-bar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-distribution-bar plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-distribution-bar'] --- import kbnSecuritySolutionDistributionBarObj from './kbn_security_solution_distribution_bar.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index 779a9911a08f..0b1037a37945 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index cdcd95b0c327..56252d485c86 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 76c56922c81c..be7ebd0a51be 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 60e6a7d2000b..ab1043d3a23e 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_security_ui_components.mdx b/api_docs/kbn_security_ui_components.mdx index 37d4c192ec91..7fa49d6c162b 100644 --- a/api_docs/kbn_security_ui_components.mdx +++ b/api_docs/kbn_security_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-ui-components title: "@kbn/security-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-ui-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-ui-components'] --- import kbnSecurityUiComponentsObj from './kbn_security_ui_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 533834a07820..3f42189484f9 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index a5a76c2a621d..e53c5eed42c8 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index f9d9670bbfe1..cf035893cf01 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index b096260aab33..aad0fae8d8e1 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 0e8043dad977..5633cc6d7e02 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 93a50d11b4c9..8f657380a69a 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index efa99b0f6f00..ec3f360c09e2 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 35770f9043be..2b217fc98c40 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 34ad20adf6ee..878a65839bfe 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index a25185f6695a..ea0ff8468331 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index cc960796a474..0488ac71876c 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index cf4f6d047dd6..f7ad8bea1f46 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 38ae43c653dc..0367555d973d 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index c68f97722f99..9d0cc192b609 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index aa0e2e5fcab6..b240f2c990ec 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index cd7297ac80d4..9be0aebca5bb 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 8beee33eb735..d5283e51a4cc 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 1eff127b3a06..b8ee88710751 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 9df4eec1e987..8ceead4e3a7f 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_client.mdx b/api_docs/kbn_server_route_repository_client.mdx index d1bb9f0ad1f3..6dba800e4fdb 100644 --- a/api_docs/kbn_server_route_repository_client.mdx +++ b/api_docs/kbn_server_route_repository_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-client title: "@kbn/server-route-repository-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-client plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-client'] --- import kbnServerRouteRepositoryClientObj from './kbn_server_route_repository_client.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_utils.mdx b/api_docs/kbn_server_route_repository_utils.mdx index 02de1d6184fa..2196a4bc7b4c 100644 --- a/api_docs/kbn_server_route_repository_utils.mdx +++ b/api_docs/kbn_server_route_repository_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-utils title: "@kbn/server-route-repository-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-utils'] --- import kbnServerRouteRepositoryUtilsObj from './kbn_server_route_repository_utils.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 869fd4cd64ba..0c5fe48102b3 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index 512562032c82..c53bdd719a67 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 48f452280e97..cb345cadee06 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 787f084c3196..a98951ad472d 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 7f3d632b9381..6277489025c9 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index dbc0c11908b8..d401be14dd28 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 8d0d49646410..bc6f940e801e 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index a9f8d59e328c..4df27618fc84 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index e8b7c6946e11..52ac2d321f70 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 15a425bf79b6..bcd7c251dc32 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 961ddedb727c..2d1a9fba09bc 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index a30c2e0f0e51..2e1eebaeeb40 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 2ea435507db2..49efcf650726 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 48f71a31cb28..4005ee46a345 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 38845ac2c98f..2eb3dd62aaab 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 4648f38b0362..7bc7d631581f 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 5c2de245d2a9..12ae3a987c04 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index c3018601c7aa..58741471353f 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index dabdaa1440d4..e12e520c65aa 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 8361002df79e..f594b69d93e5 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 4b4d13d6babd..69c62f3469c2 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 6ca2bef2f70e..fe4e8b450057 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index a594cb82773e..5b9d0f3e9707 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 9027ddd9d707..366b13fad700 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 6db0d2a53d65..90438320f47b 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 622fc1662686..123431a8be24 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 392a19c0ff2b..d601c44e161c 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 3cd5fdd946c7..ce5b632cc677 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 36f53f7fb26b..868bc47939dd 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 23584cbd7593..6a30f35b896a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 78030bee3ea6..ae3ebda7ccc3 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 816bd076e2f9..b08c2aeeb6b4 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index aac92dca8e4e..e7ad6701c0ec 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 14bf07e7285d..3b530e1d3504 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index c1f1afdfce47..9bde826c3086 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index 4a59f5fcb7e6..6dae878b9b36 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 61432bb1cb27..e7e40f494699 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 51df30ca370e..7e8fb4207f48 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 2e4d656d6cfa..3c65e02bc8af 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index a2e464a5f3bc..3da736728a33 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 737b8ee82760..13337242d1f9 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 9e46c20d39e3..12fd4e5b08cf 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 1447daf70142..2154e28257be 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index a4bb5abc40eb..0213b6758069 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index fba7362d90ec..c62fa01ec55f 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_table_persist.mdx b/api_docs/kbn_shared_ux_table_persist.mdx index ed3afe540319..e7f71b4202e1 100644 --- a/api_docs/kbn_shared_ux_table_persist.mdx +++ b/api_docs/kbn_shared_ux_table_persist.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-table-persist title: "@kbn/shared-ux-table-persist" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-table-persist plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-table-persist'] --- import kbnSharedUxTablePersistObj from './kbn_shared_ux_table_persist.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index d1e21d7f7a4b..a1dd4752be3c 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 7f701dd4df27..65288d331330 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 99a6d41a30c2..4ce54ed82db6 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index b3889f82599b..bb4311fbbe76 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_sse_utils.mdx b/api_docs/kbn_sse_utils.mdx index 205346bf5c08..5f1f5f18cde6 100644 --- a/api_docs/kbn_sse_utils.mdx +++ b/api_docs/kbn_sse_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils title: "@kbn/sse-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils'] --- import kbnSseUtilsObj from './kbn_sse_utils.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_client.mdx b/api_docs/kbn_sse_utils_client.mdx index 39bb1195eb7d..3d544b8d6aec 100644 --- a/api_docs/kbn_sse_utils_client.mdx +++ b/api_docs/kbn_sse_utils_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-client title: "@kbn/sse-utils-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-client plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-client'] --- import kbnSseUtilsClientObj from './kbn_sse_utils_client.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_server.mdx b/api_docs/kbn_sse_utils_server.mdx index a952d8edf93a..fba0e8dac86a 100644 --- a/api_docs/kbn_sse_utils_server.mdx +++ b/api_docs/kbn_sse_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-server title: "@kbn/sse-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-server plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-server'] --- import kbnSseUtilsServerObj from './kbn_sse_utils_server.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 15dd7e55782b..91f1966b7116 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 8e4d119dcefe..2acd7cb283d4 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 4af5ab6dee0b..1ccfe00076c4 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_synthetics_e2e.mdx b/api_docs/kbn_synthetics_e2e.mdx index 8c8b776b9b45..bbe9eaf09a28 100644 --- a/api_docs/kbn_synthetics_e2e.mdx +++ b/api_docs/kbn_synthetics_e2e.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-e2e title: "@kbn/synthetics-e2e" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-e2e plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-e2e'] --- import kbnSyntheticsE2eObj from './kbn_synthetics_e2e.devdocs.json'; diff --git a/api_docs/kbn_synthetics_private_location.mdx b/api_docs/kbn_synthetics_private_location.mdx index 18fea93d5425..bdfd0bec4dcc 100644 --- a/api_docs/kbn_synthetics_private_location.mdx +++ b/api_docs/kbn_synthetics_private_location.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-private-location title: "@kbn/synthetics-private-location" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-private-location plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-private-location'] --- import kbnSyntheticsPrivateLocationObj from './kbn_synthetics_private_location.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 7a6b63ecd412..55dce0b7c0d9 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index a15de1f6a556..832fc1690f20 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 3d9768a9ee34..7fafe9cc32bc 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.devdocs.json b/api_docs/kbn_test_jest_helpers.devdocs.json index a38564069270..805e169ca39b 100644 --- a/api_docs/kbn_test_jest_helpers.devdocs.json +++ b/api_docs/kbn_test_jest_helpers.devdocs.json @@ -1006,7 +1006,7 @@ "label": "renderReactTestingLibraryWithI18n", "description": [], "signature": [ - "(ui: React.ReactElement>, options?: Omit<", + "(ui: React.ReactNode, options?: Omit<", "RenderOptions", ">, options?: Omit<", + "[ui: React.ReactNode, options?: Omit<", "RenderOptions", " | undefined; highlight?: Record | undefined; inner_hits?: Record | undefined; matched_queries?: string[] | undefined; _nested?: ", + "> | undefined; matched_queries?: string[] | Record | undefined; _nested?: ", "SearchNestedIdentity", - " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; sort?: ", + " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; sort?: ", "SortResults", " | undefined; }>; find: (findParams: { query?: string | undefined; start?: string | undefined; end?: string | undefined; sloId?: string | undefined; sloInstanceId?: string | undefined; serviceName?: string | undefined; filter?: string | undefined; size?: number | undefined; }) => Promise<{ items: { id: string | undefined; annotation: { title: string; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }[]; total: number; }>; delete: (deleteParams: { id: string; }) => Promise<", "DeleteByQueryResponse", @@ -12817,9 +12817,9 @@ "ExplainExplanation", " | undefined; fields?: Record | undefined; highlight?: Record | undefined; inner_hits?: Record | undefined; matched_queries?: string[] | undefined; _nested?: ", + "> | undefined; matched_queries?: string[] | Record | undefined; _nested?: ", "SearchNestedIdentity", - " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; sort?: ", + " | undefined; _ignored?: string[] | undefined; ignored_field_values?: Record | undefined; _shard?: string | undefined; _node?: string | undefined; _routing?: string | undefined; _rank?: number | undefined; _seq_no?: number | undefined; _primary_term?: number | undefined; _version?: number | undefined; sort?: ", "SortResults", " | undefined; }>; find: (findParams: { query?: string | undefined; start?: string | undefined; end?: string | undefined; sloId?: string | undefined; sloInstanceId?: string | undefined; serviceName?: string | undefined; filter?: string | undefined; size?: number | undefined; }) => Promise<{ items: { id: string | undefined; annotation: { title: string; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }[]; total: number; }>; delete: (deleteParams: { id: string; }) => Promise<", "DeleteByQueryResponse", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index d80f739ea4ec..9f014ee4f504 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 3f5579c41a4d..a5697831bc67 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 9a6b43e0a175..074b93913966 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 24751d9e89f5..7db6d8acbec8 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 2fa25d54b86e..096d438580b0 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 2386b5450ba8..49e61b7484f5 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 64a73a489b4e..9c817ee1958b 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index c5287111fa89..d8542324ad38 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index 97772c39311e..5e4cdb40148c 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index daa9a785426a..450e68bfc2f4 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 878 | 750 | 45 | +| 879 | 751 | 45 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 54142 | 240 | 40627 | 2000 | +| 54217 | 240 | 40700 | 2000 | ## Plugin Directory @@ -69,7 +69,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 35 | 0 | 33 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | A stateful layer to register shared features and provide an access point to discover without a direct dependency | 16 | 0 | 15 | 2 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | -| | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | Server APIs for the Elastic AI Assistant | 52 | 0 | 37 | 2 | +| | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | Server APIs for the Elastic AI Assistant | 53 | 0 | 38 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 578 | 1 | 468 | 9 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 19 | 0 | 19 | 2 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 54 | 0 | 47 | 1 | @@ -184,7 +184,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 18 | 0 | 18 | 0 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 11 | 0 | 7 | 1 | | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | Plugin to provide access to and rendering of python notebooks for use in the persistent developer console. | 10 | 0 | 10 | 1 | -| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 21 | 0 | 15 | 1 | +| | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 22 | 0 | 16 | 1 | | searchprofiler | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 455 | 0 | 238 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 188 | 0 | 120 | 33 | @@ -235,7 +235,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the vislib visualizations. These are the classical area/line/bar, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 1 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 52 | 0 | 50 | 5 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 872 | 12 | 841 | 20 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 871 | 12 | 840 | 20 | | watcher | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | ## Package Directory @@ -547,6 +547,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 0 | 27 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 7 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 47 | 0 | 40 | 0 | +| | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 75 | 0 | 73 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 124 | 3 | 124 | 0 | | | [@elastic/appex-ai-infra](https://github.com/orgs/elastic/teams/appex-ai-infra) | - | 121 | 0 | 38 | 1 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 7 | 1 | 7 | 1 | @@ -575,7 +576,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 23 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 2 | 3 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 45 | 0 | 0 | 0 | -| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 140 | 0 | 139 | 0 | +| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 139 | 0 | 138 | 0 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 20 | 0 | 11 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index f9ecadc3d4d0..0b7f9c31a9ea 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index ab330280b81f..206ed505ff73 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 07d33ef2fa52..bf012bba8d61 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 1f2f98966446..d369f956a35e 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 07cd882b8011..90127ece37af 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index c1f9d099d528..fe3bdc4f656c 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index fadca7cb9bdc..2f5444d4041c 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 9921188605b3..33ff7e05af80 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 7566e69c16f2..12e54ddd2495 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 491b87130091..3e3a002659d5 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 5d94314705e0..5d82125c05cc 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 118c24136f5e..393bb25c35ec 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 75a6b4174e58..fb96c0c1637c 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index ead5baa3d5ac..9a86f855006d 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 738c02dad496..7aa957856f6f 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 71f5ab557158..ecc45f81df2a 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 24fa328a7556..658acfa48d51 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_assistant.mdx b/api_docs/search_assistant.mdx index 7c1f19f27d53..ad278f7e2714 100644 --- a/api_docs/search_assistant.mdx +++ b/api_docs/search_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchAssistant title: "searchAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the searchAssistant plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchAssistant'] --- import searchAssistantObj from './search_assistant.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index dfe86ece22bf..5c9b12c2ad9c 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index 6e389b22c592..b19d497edcd8 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_indices.devdocs.json b/api_docs/search_indices.devdocs.json index 2a5d681533d1..11be05829ab5 100644 --- a/api_docs/search_indices.devdocs.json +++ b/api_docs/search_indices.devdocs.json @@ -85,7 +85,7 @@ "label": "startAppId", "description": [], "signature": [ - "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" + "\"fleet\" | \"graph\" | \"ml\" | \"monitoring\" | \"profiling\" | \"metrics\" | \"management\" | \"apm\" | \"synthetics\" | \"ux\" | \"canvas\" | \"logs\" | \"dashboards\" | \"slo\" | \"observabilityAIAssistant\" | \"home\" | \"integrations\" | \"discover\" | \"observability-overview\" | \"appSearch\" | \"dev_tools\" | \"maps\" | \"visualize\" | \"dev_tools:console\" | \"dev_tools:searchprofiler\" | \"dev_tools:painless_lab\" | \"dev_tools:grokdebugger\" | \"ml:notifications\" | \"ml:nodes\" | \"ml:overview\" | \"ml:memoryUsage\" | \"ml:settings\" | \"ml:dataVisualizer\" | \"ml:logPatternAnalysis\" | \"ml:logRateAnalysis\" | \"ml:singleMetricViewer\" | \"ml:anomalyDetection\" | \"ml:anomalyExplorer\" | \"ml:dataDrift\" | \"ml:dataFrameAnalytics\" | \"ml:resultExplorer\" | \"ml:analyticsMap\" | \"ml:aiOps\" | \"ml:changePointDetections\" | \"ml:modelManagement\" | \"ml:nodesOverview\" | \"ml:esqlDataVisualizer\" | \"ml:fileUpload\" | \"ml:indexDataVisualizer\" | \"ml:calendarSettings\" | \"ml:filterListsSettings\" | \"ml:suppliedConfigurations\" | \"osquery\" | \"management:transform\" | \"management:watcher\" | \"management:cases\" | \"management:tags\" | \"management:maintenanceWindows\" | \"management:cross_cluster_replication\" | \"management:dataViews\" | \"management:spaces\" | \"management:settings\" | \"management:users\" | \"management:migrate_data\" | \"management:search_sessions\" | \"management:data_quality\" | \"management:filesManagement\" | \"management:roles\" | \"management:reporting\" | \"management:aiAssistantManagementSelection\" | \"management:securityAiAssistantManagement\" | \"management:observabilityAiAssistantManagement\" | \"management:api_keys\" | \"management:license_management\" | \"management:index_lifecycle_management\" | \"management:index_management\" | \"management:ingest_pipelines\" | \"management:jobsListLink\" | \"management:objects\" | \"management:pipelines\" | \"management:remote_clusters\" | \"management:role_mappings\" | \"management:rollup_jobs\" | \"management:snapshot_restore\" | \"management:triggersActions\" | \"management:triggersActionsConnectors\" | \"management:upgrade_assistant\" | \"enterpriseSearch\" | \"enterpriseSearchContent\" | \"enterpriseSearchApplications\" | \"searchInferenceEndpoints\" | \"enterpriseSearchAnalytics\" | \"workplaceSearch\" | \"serverlessElasticsearch\" | \"serverlessConnectors\" | \"searchPlayground\" | \"searchHomepage\" | \"enterpriseSearchContent:connectors\" | \"enterpriseSearchContent:searchIndices\" | \"enterpriseSearchContent:webCrawlers\" | \"enterpriseSearchApplications:searchApplications\" | \"enterpriseSearchApplications:playground\" | \"appSearch:engines\" | \"searchInferenceEndpoints:inferenceEndpoints\" | \"elasticsearchStart\" | \"elasticsearchIndices\" | \"enterpriseSearchElasticsearch\" | \"enterpriseSearchVectorSearch\" | \"enterpriseSearchSemanticSearch\" | \"enterpriseSearchAISearch\" | \"elasticsearchIndices:createIndex\" | \"observability-logs-explorer\" | \"last-used-logs-viewer\" | \"observabilityOnboarding\" | \"inventory\" | \"logs:settings\" | \"logs:stream\" | \"logs:log-categories\" | \"logs:anomalies\" | \"observability-overview:cases\" | \"observability-overview:alerts\" | \"observability-overview:rules\" | \"observability-overview:cases_create\" | \"observability-overview:cases_configure\" | \"metrics:settings\" | \"metrics:hosts\" | \"metrics:inventory\" | \"metrics:metrics-explorer\" | \"metrics:assetDetails\" | \"apm:services\" | \"apm:traces\" | \"apm:dependencies\" | \"apm:service-map\" | \"apm:settings\" | \"apm:service-groups-list\" | \"apm:storage-explorer\" | \"synthetics:overview\" | \"synthetics:certificates\" | \"profiling:functions\" | \"profiling:stacktraces\" | \"profiling:flamegraphs\" | \"inventory:datastreams\" | \"securitySolutionUI\" | \"securitySolutionUI:\" | \"securitySolutionUI:cases\" | \"securitySolutionUI:alerts\" | \"securitySolutionUI:rules\" | \"securitySolutionUI:policy\" | \"securitySolutionUI:overview\" | \"securitySolutionUI:dashboards\" | \"securitySolutionUI:kubernetes\" | \"securitySolutionUI:cases_create\" | \"securitySolutionUI:cases_configure\" | \"securitySolutionUI:hosts\" | \"securitySolutionUI:users\" | \"securitySolutionUI:cloud_defend-policies\" | \"securitySolutionUI:cloud_security_posture-dashboard\" | \"securitySolutionUI:cloud_security_posture-findings\" | \"securitySolutionUI:cloud_security_posture-benchmarks\" | \"securitySolutionUI:network\" | \"securitySolutionUI:data_quality\" | \"securitySolutionUI:explore\" | \"securitySolutionUI:assets\" | \"securitySolutionUI:cloud_defend\" | \"securitySolutionUI:notes\" | \"securitySolutionUI:administration\" | \"securitySolutionUI:attack_discovery\" | \"securitySolutionUI:blocklist\" | \"securitySolutionUI:cloud_security_posture-rules\" | \"securitySolutionUI:detections\" | \"securitySolutionUI:detection_response\" | \"securitySolutionUI:endpoints\" | \"securitySolutionUI:event_filters\" | \"securitySolutionUI:exceptions\" | \"securitySolutionUI:host_isolation_exceptions\" | \"securitySolutionUI:hosts-all\" | \"securitySolutionUI:hosts-anomalies\" | \"securitySolutionUI:hosts-risk\" | \"securitySolutionUI:hosts-events\" | \"securitySolutionUI:hosts-sessions\" | \"securitySolutionUI:hosts-uncommon_processes\" | \"securitySolutionUI:investigations\" | \"securitySolutionUI:get_started\" | \"securitySolutionUI:machine_learning-landing\" | \"securitySolutionUI:network-anomalies\" | \"securitySolutionUI:network-dns\" | \"securitySolutionUI:network-events\" | \"securitySolutionUI:network-flows\" | \"securitySolutionUI:network-http\" | \"securitySolutionUI:network-tls\" | \"securitySolutionUI:response_actions_history\" | \"securitySolutionUI:rules-add\" | \"securitySolutionUI:rules-create\" | \"securitySolutionUI:rules-landing\" | \"securitySolutionUI:threat_intelligence\" | \"securitySolutionUI:timelines\" | \"securitySolutionUI:timelines-templates\" | \"securitySolutionUI:trusted_apps\" | \"securitySolutionUI:users-all\" | \"securitySolutionUI:users-anomalies\" | \"securitySolutionUI:users-authentications\" | \"securitySolutionUI:users-events\" | \"securitySolutionUI:users-risk\" | \"securitySolutionUI:entity_analytics\" | \"securitySolutionUI:entity_analytics-management\" | \"securitySolutionUI:entity_analytics-asset-classification\" | \"securitySolutionUI:entity_analytics-entity_store_management\" | \"securitySolutionUI:coverage-overview\" | \"fleet:settings\" | \"fleet:agents\" | \"fleet:policies\" | \"fleet:data_streams\" | \"fleet:enrollment_tokens\" | \"fleet:uninstall_tokens\"" ], "path": "x-pack/plugins/search_indices/public/types.ts", "deprecated": false, diff --git a/api_docs/search_indices.mdx b/api_docs/search_indices.mdx index b2100c97e4e8..9f24d98efc5e 100644 --- a/api_docs/search_indices.mdx +++ b/api_docs/search_indices.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchIndices title: "searchIndices" image: https://source.unsplash.com/400x175/?github description: API docs for the searchIndices plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchIndices'] --- import searchIndicesObj from './search_indices.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index 994ccfc5b4b1..2f982d4bd9f2 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index d648b1a534cd..9c6cc120f9d5 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.devdocs.json b/api_docs/search_playground.devdocs.json index 17d790c21d72..c097d00d40a1 100644 --- a/api_docs/search_playground.devdocs.json +++ b/api_docs/search_playground.devdocs.json @@ -4,7 +4,20 @@ "classes": [], "functions": [], "interfaces": [], - "enums": [], + "enums": [ + { + "parentPluginId": "searchPlayground", + "id": "def-public.PlaygroundPageMode", + "type": "Enum", + "tags": [], + "label": "PlaygroundPageMode", + "description": [], + "path": "x-pack/plugins/search_playground/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], "misc": [], "objects": [], "setup": { diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 403fc7d682cd..e4f76b15cc21 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 21 | 0 | 15 | 1 | +| 22 | 0 | 16 | 1 | ## Client @@ -31,6 +31,9 @@ Contact [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-ki ### Start +### Enums + + ## Server ### Setup diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 34f62d1079a7..f2a9ff6f5a85 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index cf577629871c..8c458c5dd5c9 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index c4bea975988c..09498b8b4441 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index 62018e2ccdd4..e85832e528bd 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 3b1fc801bb82..834b2849bf4f 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index b9f62ae5a5cc..ab9d0dafee7d 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index 3eb9d112921d..42e3a4a3621d 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index c8d9e01e24b9..0fe1099b6475 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 0edc2ef35af6..9a754c7f4216 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 76877cd12882..25f86f6999c9 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index deee9fdb541a..0db83bcf54f2 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 31865d22e5f2..b0cd89257784 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 4cfcbc6251dc..ffe989d464af 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 103cb9696b4b..5c1c4923420b 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 70bdd783a520..3c670603eb89 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 60dbb98e2ab1..43819592c035 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 72c605c6c1df..eca438724845 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 0dabf140492d..1c5b7f448713 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 7ae72ce36d8f..b2ad8e21aff3 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index d40bd69ad446..fbacc1e90a62 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index ecedd9670305..291701282c81 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index ba5312049eed..fffd0501602d 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 6ab39442c68e..92773d148739 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 80b5b40d74e2..1222419d87c9 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 8fd6c8723645..6613e34f4365 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 3b74c67464fd..14165a4707ea 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 55716f98e200..176451a11b83 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index eda199e57b83..7f85dc30a86d 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 2b537d4507d0..868b1e28155d 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 9ff8d507cecf..45c1fcb5ea0b 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 8707950662e1..fbd8706e92a6 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 85d480beecaf..b623525fb2cf 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 2342e5aa5569..2860a5adf69c 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index ff5ef4fd5d2f..717b780a8b2b 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index c4707e4e8079..e15ce6607d5e 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 7c5e17d7aa3c..cdc9a04b9baa 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 6b3471e08614..dae1914194af 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 41f321cff8c4..cfa2d3463f78 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 38d8f5afeca7..4baff6787751 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 8726e55be833..d21887d0ab6a 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 64dbbbbf238d..11f32e53b40e 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 785f62076ab1..bb4c1e7c59b1 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index bcaf2ac9154d..d2d15673ef45 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -336,20 +336,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "visualizations", - "id": "def-public.BaseVisType.suppressWarnings", - "type": "Function", - "tags": [], - "label": "suppressWarnings", - "description": [], - "signature": [ - "(() => boolean) | undefined" - ], - "path": "src/plugins/visualizations/public/vis_types/base_vis_type.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "visualizations", "id": "def-public.BaseVisType.hasPartialRows", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index abda17709ceb..78840e906c6a 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-11-10 +date: 2024-11-12 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 872 | 12 | 841 | 20 | +| 871 | 12 | 840 | 20 | ## Client From e87fbd8c241d9327df6dc4ad1942447fa99276fb Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Nov 2024 09:03:50 +0100 Subject: [PATCH 037/100] [ES|QL] Group by histogram suggestion first in the list (#199611) ## Summary Ensures that group BY histogram suggestion is first on the list. It fixes a regression caused from the refactoring of autocomplete image ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../src/autocomplete/commands/stats/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts index c9abaa5c5408..0730b004e28d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/commands/stats/util.ts @@ -99,6 +99,6 @@ export const getDateHistogramCompletionItem: ( defaultMessage: 'Add date histogram using bucket()', } ), - sortText: '1A', + sortText: '1', command: TRIGGER_SUGGESTION_COMMAND, }); From dbc9e31dbccd5a743c85b3a52692fd950c0aa292 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Nov 2024 09:04:01 +0100 Subject: [PATCH 038/100] [ES|QL] Marks tech preview functions in the editor (#199631) ## Summary Closes https://github.com/elastic/kibana/issues/194062 Indicates tech preview functions in the editor. ![image (62)](https://github.com/user-attachments/assets/a6d2b1e8-f7c7-4bee-8a9f-3c9d5026c79e) --- .../scripts/generate_function_definitions.ts | 5 + .../src/autocomplete/factories.ts | 13 ++- .../generated/aggregation_functions.ts | 13 +++ .../definitions/generated/scalar_functions.ts | 104 +++++++++++++++++- .../src/definitions/types.ts | 1 + 5 files changed, 134 insertions(+), 2 deletions(-) diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts index fe2a85456aa1..4f649b44e44b 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts @@ -255,6 +255,7 @@ function getFunctionDefinition(ESFunctionDefinition: Record): Funct description: ESFunctionDefinition.description, alias: aliasTable[ESFunctionDefinition.name], ignoreAsSuggestion: ESFunctionDefinition.snapshot_only, + preview: ESFunctionDefinition.preview, signatures: _.uniqBy( ESFunctionDefinition.signatures.map((signature: any) => ({ ...signature, @@ -334,6 +335,7 @@ function printGeneratedFunctionsFile( description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.${name}', { defaultMessage: ${JSON.stringify( removeAsciiDocInternalCrossReferences(removeInlineAsciiDocLinks(description), functionNames) )} }),${functionDefinition.ignoreAsSuggestion ? 'ignoreAsSuggestion: true,\n' : ''} + preview: ${functionDefinition.preview || 'false'}, alias: ${alias ? `['${alias.join("', '")}']` : 'undefined'}, signatures: ${JSON.stringify(signatures, null, 2)}, supportedCommands: ${JSON.stringify(functionDefinition.supportedCommands)}, @@ -393,6 +395,9 @@ import { isLiteralItem } from '../../shared/helpers';` (async function main() { const pathToElasticsearch = process.argv[2]; + if (!pathToElasticsearch) { + throw new Error('Path to Elasticsearch is required'); + } const ESFunctionDefinitionsDirectory = join( pathToElasticsearch, diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts index 88560f6d2f4c..d9813bb0e91a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts @@ -30,6 +30,13 @@ import { isNumericType } from '../shared/esql_types'; import { getTestFunctions } from '../shared/test_functions'; import { builtinFunctions } from '../definitions/builtin'; +const techPreviewLabel = i18n.translate( + 'kbn-esql-validation-autocomplete.esql.autocomplete.techPreviewLabel', + { + defaultMessage: `Technical Preview`, + } +); + const allFunctions = memoize( () => aggregationFunctionDefinitions @@ -60,13 +67,17 @@ function getSafeInsertSourceText(text: string) { } export function getFunctionSuggestion(fn: FunctionDefinition): SuggestionRawDefinition { + let detail = fn.description; + if (fn.preview) { + detail = `[${techPreviewLabel}] ${detail}`; + } const fullSignatures = getFunctionSignatures(fn, { capitalize: true, withTypes: true }); return { label: fn.name.toUpperCase(), text: `${fn.name.toUpperCase()}($0)`, asSnippet: true, kind: 'Function', - detail: fn.description, + detail, documentation: { value: buildFunctionDocumentation(fullSignatures, fn.examples), }, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/aggregation_functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/aggregation_functions.ts index f73521ddf3a8..6a429f2288f9 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/aggregation_functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/aggregation_functions.ts @@ -36,6 +36,7 @@ const avgDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.avg', { defaultMessage: 'The average of a numeric field.', }), + preview: false, alias: undefined, signatures: [ { @@ -85,6 +86,7 @@ const countDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.count', { defaultMessage: 'Returns the total number (count) of input values.', }), + preview: false, alias: undefined, signatures: [ { @@ -228,6 +230,7 @@ const countDistinctDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.count_distinct', { defaultMessage: 'Returns the approximate number of distinct values.', }), + preview: false, alias: undefined, signatures: [ { @@ -743,6 +746,7 @@ const maxDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.max', { defaultMessage: 'The maximum value of a field.', }), + preview: false, alias: undefined, signatures: [ { @@ -853,6 +857,7 @@ const medianDefinition: FunctionDefinition = { defaultMessage: 'The value that is greater than half of all values and less than half of all values, also known as the 50% `PERCENTILE`.', }), + preview: false, alias: undefined, signatures: [ { @@ -906,6 +911,7 @@ const medianAbsoluteDeviationDefinition: FunctionDefinition = { "Returns the median absolute deviation, a measure of variability. It is a robust statistic, meaning that it is useful for describing data that may have outliers, or may not be normally distributed. For such data it can be more descriptive than standard deviation.\n\nIt is calculated as the median of each data point's deviation from the median of the entire sample. That is, for a random variable `X`, the median absolute deviation is `median(|median(X) - X|)`.", } ), + preview: false, alias: undefined, signatures: [ { @@ -955,6 +961,7 @@ const minDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.min', { defaultMessage: 'The minimum value of a field.', }), + preview: false, alias: undefined, signatures: [ { @@ -1065,6 +1072,7 @@ const percentileDefinition: FunctionDefinition = { defaultMessage: 'Returns the value at which a certain percentage of observed values occur. For example, the 95th percentile is the value which is greater than 95% of the observed values and the 50th percentile is the `MEDIAN`.', }), + preview: false, alias: undefined, signatures: [ { @@ -1228,6 +1236,7 @@ const stCentroidAggDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.st_centroid_agg', { defaultMessage: 'Calculate the spatial centroid over a field with spatial point geometry type.', }), + preview: false, alias: undefined, signatures: [ { @@ -1264,6 +1273,7 @@ const sumDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.sum', { defaultMessage: 'The sum of a numeric expression.', }), + preview: false, alias: undefined, signatures: [ { @@ -1313,6 +1323,7 @@ const topDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.top', { defaultMessage: 'Collects the top values for a field. Includes repeated values.', }), + preview: false, alias: undefined, signatures: [ { @@ -1510,6 +1521,7 @@ const valuesDefinition: FunctionDefinition = { defaultMessage: "Returns all values in a group as a multivalued field. The order of the returned values isn't guaranteed. If you need the values returned in order use esql-mv_sort.", }), + preview: true, alias: undefined, signatures: [ { @@ -1618,6 +1630,7 @@ const weightedAvgDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.weighted_avg', { defaultMessage: 'The weighted average of a numeric expression.', }), + preview: false, alias: undefined, signatures: [ { diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts index 739a12095ac2..7e9019aeb905 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts @@ -38,6 +38,7 @@ const absDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.abs', { defaultMessage: 'Returns the absolute value.', }), + preview: false, alias: undefined, signatures: [ { @@ -97,6 +98,7 @@ const acosDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.acos', { defaultMessage: 'Returns the arccosine of `n` as an angle, expressed in radians.', }), + preview: false, alias: undefined, signatures: [ { @@ -154,6 +156,7 @@ const asinDefinition: FunctionDefinition = { defaultMessage: 'Returns the arcsine of the input\nnumeric expression as an angle, expressed in radians.', }), + preview: false, alias: undefined, signatures: [ { @@ -211,6 +214,7 @@ const atanDefinition: FunctionDefinition = { defaultMessage: 'Returns the arctangent of the input\nnumeric expression as an angle, expressed in radians.', }), + preview: false, alias: undefined, signatures: [ { @@ -268,6 +272,7 @@ const atan2Definition: FunctionDefinition = { defaultMessage: 'The angle between the positive x-axis and the ray from the\norigin to the point (x , y) in the Cartesian plane, expressed in radians.', }), + preview: false, alias: undefined, signatures: [ { @@ -526,6 +531,7 @@ const categorizeDefinition: FunctionDefinition = { }), ignoreAsSuggestion: true, + preview: false, alias: undefined, signatures: [ { @@ -563,6 +569,7 @@ const cbrtDefinition: FunctionDefinition = { defaultMessage: 'Returns the cube root of a number. The input can be any numeric value, the return value is always a double.\nCube roots of infinities are null.', }), + preview: false, alias: undefined, signatures: [ { @@ -619,6 +626,7 @@ const ceilDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.ceil', { defaultMessage: 'Round a number up to the nearest integer.', }), + preview: false, alias: undefined, signatures: [ { @@ -676,6 +684,7 @@ const cidrMatchDefinition: FunctionDefinition = { defaultMessage: 'Returns true if the provided IP is contained in one of the provided CIDR blocks.', }), + preview: false, alias: undefined, signatures: [ { @@ -727,6 +736,7 @@ const coalesceDefinition: FunctionDefinition = { defaultMessage: 'Returns the first of its arguments that is not null. If all arguments are null, it returns `null`.', }), + preview: false, alias: undefined, signatures: [ { @@ -990,6 +1000,7 @@ const concatDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.concat', { defaultMessage: 'Concatenates two or more strings.', }), + preview: false, alias: undefined, signatures: [ { @@ -1072,6 +1083,7 @@ const cosDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.cos', { defaultMessage: 'Returns the cosine of an angle.', }), + preview: false, alias: undefined, signatures: [ { @@ -1128,6 +1140,7 @@ const coshDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.cosh', { defaultMessage: 'Returns the hyperbolic cosine of a number.', }), + preview: false, alias: undefined, signatures: [ { @@ -1185,6 +1198,7 @@ const dateDiffDefinition: FunctionDefinition = { defaultMessage: 'Subtracts the `startTimestamp` from the `endTimestamp` and returns the difference in multiples of `unit`.\nIf `startTimestamp` is later than the `endTimestamp`, negative values are returned.', }), + preview: false, alias: undefined, signatures: [ { @@ -1305,6 +1319,7 @@ const dateExtractDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.date_extract', { defaultMessage: 'Extracts parts of a date, like year, month, day, hour.', }), + preview: false, alias: undefined, signatures: [ { @@ -1386,6 +1401,7 @@ const dateFormatDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.date_format', { defaultMessage: 'Returns a string representation of a date, in the provided format.', }), + preview: false, alias: undefined, signatures: [ { @@ -1435,6 +1451,7 @@ const dateParseDefinition: FunctionDefinition = { defaultMessage: 'Returns a date by parsing the second argument using the format specified in the first argument.', }), + preview: false, alias: undefined, signatures: [ { @@ -1511,6 +1528,7 @@ const dateTruncDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.date_trunc', { defaultMessage: 'Rounds down a date to the closest interval.', }), + preview: false, alias: undefined, signatures: [ { @@ -1561,6 +1579,7 @@ const eDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.e', { defaultMessage: "Returns Euler's number.", }), + preview: false, alias: undefined, signatures: [ { @@ -1582,6 +1601,7 @@ const endsWithDefinition: FunctionDefinition = { defaultMessage: 'Returns a boolean that indicates whether a keyword string ends with another string.', }), + preview: false, alias: undefined, signatures: [ { @@ -1658,6 +1678,7 @@ const expDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.exp', { defaultMessage: 'Returns the value of e raised to the power of the given number.', }), + preview: false, alias: undefined, signatures: [ { @@ -1714,6 +1735,7 @@ const floorDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.floor', { defaultMessage: 'Round a number down to the nearest integer.', }), + preview: false, alias: undefined, signatures: [ { @@ -1770,6 +1792,7 @@ const fromBase64Definition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.from_base64', { defaultMessage: 'Decode a base64 string.', }), + preview: false, alias: undefined, signatures: [ { @@ -1807,6 +1830,7 @@ const greatestDefinition: FunctionDefinition = { defaultMessage: 'Returns the maximum value from multiple columns. This is similar to `MV_MAX`\nexcept it is intended to run on multiple columns at once.', }), + preview: false, alias: undefined, signatures: [ { @@ -2023,6 +2047,7 @@ const hypotDefinition: FunctionDefinition = { defaultMessage: 'Returns the hypotenuse of two numbers. The input can be any numeric values, the return value is always a double.\nHypotenuses of infinities are null.', }), + preview: false, alias: undefined, signatures: [ { @@ -2279,6 +2304,7 @@ const ipPrefixDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.ip_prefix', { defaultMessage: 'Truncates an IP to a given prefix length.', }), + preview: false, alias: undefined, signatures: [ { @@ -2318,6 +2344,7 @@ const leastDefinition: FunctionDefinition = { defaultMessage: 'Returns the minimum value from multiple columns. This is similar to `MV_MIN` except it is intended to run on multiple columns at once.', }), + preview: false, alias: undefined, signatures: [ { @@ -2534,6 +2561,7 @@ const leftDefinition: FunctionDefinition = { defaultMessage: "Returns the substring that extracts 'length' chars from 'string' starting from the left.", }), + preview: false, alias: undefined, signatures: [ { @@ -2582,6 +2610,7 @@ const lengthDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.length', { defaultMessage: 'Returns the character length of a string.', }), + preview: false, alias: undefined, signatures: [ { @@ -2619,6 +2648,7 @@ const locateDefinition: FunctionDefinition = { defaultMessage: 'Returns an integer that indicates the position of a keyword substring within another string.\nReturns `0` if the substring cannot be found.\nNote that string positions start from `1`.', }), + preview: false, alias: undefined, signatures: [ { @@ -2776,6 +2806,7 @@ const logDefinition: FunctionDefinition = { defaultMessage: 'Returns the logarithm of a value to a base. The input can be any numeric value, the return value is always a double.\n\nLogs of zero, negative numbers, and base of one return `null` as well as a warning.', }), + preview: false, alias: undefined, signatures: [ { @@ -3099,6 +3130,7 @@ const log10Definition: FunctionDefinition = { defaultMessage: 'Returns the logarithm of a value to base 10. The input can be any numeric value, the return value is always a double.\n\nLogs of 0 and negative numbers return `null` as well as a warning.', }), + preview: false, alias: undefined, signatures: [ { @@ -3178,6 +3210,7 @@ const ltrimDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.ltrim', { defaultMessage: 'Removes leading whitespaces from a string.', }), + preview: false, alias: undefined, signatures: [ { @@ -3217,6 +3250,7 @@ const matchDefinition: FunctionDefinition = { defaultMessage: 'Performs a match query on the specified field. Returns true if the provided query matches the row.', }), + preview: true, alias: undefined, signatures: [ { @@ -3295,6 +3329,7 @@ const mvAppendDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.mv_append', { defaultMessage: 'Concatenates values of two multi-value fields.', }), + preview: false, alias: undefined, signatures: [ { @@ -3507,6 +3542,7 @@ const mvAvgDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued field into a single valued field containing the average of all of the values.', }), + preview: false, alias: undefined, signatures: [ { @@ -3564,6 +3600,7 @@ const mvConcatDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued string expression into a single valued column containing the concatenation of all values separated by a delimiter.', }), + preview: false, alias: undefined, signatures: [ { @@ -3644,6 +3681,7 @@ const mvCountDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued expression into a single valued column containing a count of the number of values.', }), + preview: false, alias: undefined, signatures: [ { @@ -3800,6 +3838,7 @@ const mvDedupeDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.mv_dedupe', { defaultMessage: 'Remove duplicate values from a multivalued field.', }), + preview: false, alias: undefined, signatures: [ { @@ -3947,6 +3986,7 @@ const mvFirstDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued expression into a single valued column containing the\nfirst value. This is most useful when reading from a function that emits\nmultivalued columns in a known order like `SPLIT`.', }), + preview: false, alias: undefined, signatures: [ { @@ -4104,6 +4144,7 @@ const mvLastDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalue expression into a single valued column containing the last\nvalue. This is most useful when reading from a function that emits multivalued\ncolumns in a known order like `SPLIT`.', }), + preview: false, alias: undefined, signatures: [ { @@ -4261,6 +4302,7 @@ const mvMaxDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued expression into a single valued column containing the maximum value.', }), + preview: false, alias: undefined, signatures: [ { @@ -4381,6 +4423,7 @@ const mvMedianDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued field into a single valued field containing the median value.', }), + preview: false, alias: undefined, signatures: [ { @@ -4444,6 +4487,7 @@ const mvMedianAbsoluteDeviationDefinition: FunctionDefinition = { "Converts a multivalued field into a single valued field containing the median absolute deviation.\n\nIt is calculated as the median of each data point's deviation from the median of the entire sample. That is, for a random variable `X`, the median absolute deviation is `median(|median(X) - X|)`.", } ), + preview: false, alias: undefined, signatures: [ { @@ -4503,6 +4547,7 @@ const mvMinDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued expression into a single valued column containing the minimum value.', }), + preview: false, alias: undefined, signatures: [ { @@ -4623,6 +4668,7 @@ const mvPercentileDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued field into a single valued field containing the value at which a certain percentage of observed values occur.', }), + preview: false, alias: undefined, signatures: [ { @@ -4780,6 +4826,7 @@ const mvPseriesWeightedSumDefinition: FunctionDefinition = { 'Converts a multivalued expression into a single-valued column by multiplying every element on the input list by its corresponding term in P-Series and computing the sum.', } ), + preview: false, alias: undefined, signatures: [ { @@ -4814,6 +4861,7 @@ const mvSliceDefinition: FunctionDefinition = { defaultMessage: 'Returns a subset of the multivalued field using the start and end index values.\nThis is most useful when reading from a function that emits multivalued columns\nin a known order like `SPLIT` or `MV_SORT`.', }), + preview: false, alias: undefined, signatures: [ { @@ -5093,6 +5141,7 @@ const mvSortDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.mv_sort', { defaultMessage: 'Sorts a multivalued field in lexicographical order.', }), + preview: false, alias: undefined, signatures: [ { @@ -5254,6 +5303,7 @@ const mvSumDefinition: FunctionDefinition = { defaultMessage: 'Converts a multivalued field into a single valued field containing the sum of all of the values.', }), + preview: false, alias: undefined, signatures: [ { @@ -5311,6 +5361,7 @@ const mvZipDefinition: FunctionDefinition = { defaultMessage: 'Combines the values from two multivalued fields with a delimiter that joins them together.', }), + preview: false, alias: undefined, signatures: [ { @@ -5549,6 +5600,7 @@ const nowDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.now', { defaultMessage: 'Returns current date and time.', }), + preview: false, alias: undefined, signatures: [ { @@ -5569,6 +5621,7 @@ const piDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.pi', { defaultMessage: "Returns Pi, the ratio of a circle's circumference to its diameter.", }), + preview: false, alias: undefined, signatures: [ { @@ -5589,6 +5642,7 @@ const powDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.pow', { defaultMessage: 'Returns the value of `base` raised to the power of `exponent`.', }), + preview: false, alias: undefined, signatures: [ { @@ -5849,6 +5903,7 @@ const qstrDefinition: FunctionDefinition = { defaultMessage: 'Performs a query string query. Returns true if the provided query string matches the row.', }), + preview: true, alias: undefined, signatures: [ { @@ -5888,6 +5943,7 @@ const repeatDefinition: FunctionDefinition = { defaultMessage: 'Returns a string constructed by concatenating `string` with itself the specified `number` of times.', }), + preview: false, alias: undefined, signatures: [ { @@ -5924,7 +5980,7 @@ const repeatDefinition: FunctionDefinition = { supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, - examples: ['ROW a = "Hello!"\n| EVAL triple_a = REPEAT(a, 3);'], + examples: ['ROW a = "Hello!"\n| EVAL triple_a = REPEAT(a, 3)'], }; // Do not edit this manually... generated by scripts/generate_function_definitions.ts @@ -5935,6 +5991,7 @@ const replaceDefinition: FunctionDefinition = { defaultMessage: 'The function substitutes in the string `str` any match of the regular expression `regex`\nwith the replacement string `newStr`.', }), + preview: false, alias: undefined, signatures: [ { @@ -6111,6 +6168,7 @@ const reverseDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.reverse', { defaultMessage: 'Returns a new string representing the input string in reverse order.', }), + preview: false, alias: undefined, signatures: [ { @@ -6151,6 +6209,7 @@ const rightDefinition: FunctionDefinition = { defaultMessage: "Return the substring that extracts 'length' chars from 'str' starting from the right.", }), + preview: false, alias: undefined, signatures: [ { @@ -6200,6 +6259,7 @@ const roundDefinition: FunctionDefinition = { defaultMessage: 'Rounds a number to the specified number of decimal places.\nDefaults to 0, which returns the nearest integer. If the\nprecision is a negative number, rounds to the number of digits left\nof the decimal point.', }), + preview: false, alias: undefined, signatures: [ { @@ -6303,6 +6363,7 @@ const rtrimDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.rtrim', { defaultMessage: 'Removes trailing whitespaces from a string.', }), + preview: false, alias: undefined, signatures: [ { @@ -6342,6 +6403,7 @@ const signumDefinition: FunctionDefinition = { defaultMessage: 'Returns the sign of the given number.\nIt returns `-1` for negative numbers, `0` for `0` and `1` for positive numbers.', }), + preview: false, alias: undefined, signatures: [ { @@ -6398,6 +6460,7 @@ const sinDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.sin', { defaultMessage: 'Returns the sine of an angle.', }), + preview: false, alias: undefined, signatures: [ { @@ -6454,6 +6517,7 @@ const sinhDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.sinh', { defaultMessage: 'Returns the hyperbolic sine of a number.', }), + preview: false, alias: undefined, signatures: [ { @@ -6510,6 +6574,7 @@ const spaceDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.space', { defaultMessage: 'Returns a string made of `number` spaces.', }), + preview: false, alias: undefined, signatures: [ { @@ -6536,6 +6601,7 @@ const splitDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.split', { defaultMessage: 'Split a single valued string into multiple strings.', }), + preview: false, alias: undefined, signatures: [ { @@ -6613,6 +6679,7 @@ const sqrtDefinition: FunctionDefinition = { defaultMessage: 'Returns the square root of a number. The input can be any numeric value, the return value is always a double.\nSquare roots of negative numbers and infinities are null.', }), + preview: false, alias: undefined, signatures: [ { @@ -6670,6 +6737,7 @@ const stContainsDefinition: FunctionDefinition = { defaultMessage: 'Returns whether the first geometry contains the second geometry.\nThis is the inverse of the `ST_WITHIN` function.', }), + preview: false, alias: undefined, signatures: [ { @@ -6809,6 +6877,7 @@ const stDisjointDefinition: FunctionDefinition = { defaultMessage: 'Returns whether the two geometries or geometry columns are disjoint.\nThis is the inverse of the `ST_INTERSECTS` function.\nIn mathematical terms: ST_Disjoint(A, B) ⇔ A ⋂ B = ∅', }), + preview: false, alias: undefined, signatures: [ { @@ -6948,6 +7017,7 @@ const stDistanceDefinition: FunctionDefinition = { defaultMessage: 'Computes the distance between two points.\nFor cartesian geometries, this is the pythagorean distance in the same units as the original coordinates.\nFor geographic geometries, this is the circular distance along the great circle in meters.', }), + preview: false, alias: undefined, signatures: [ { @@ -6997,6 +7067,7 @@ const stIntersectsDefinition: FunctionDefinition = { defaultMessage: 'Returns true if two geometries intersect.\nThey intersect if they have any point in common, including their interior points\n(points along lines or within polygons).\nThis is the inverse of the `ST_DISJOINT` function.\nIn mathematical terms: ST_Intersects(A, B) ⇔ A ⋂ B ≠ ∅', }), + preview: false, alias: undefined, signatures: [ { @@ -7136,6 +7207,7 @@ const stWithinDefinition: FunctionDefinition = { defaultMessage: 'Returns whether the first geometry is within the second geometry.\nThis is the inverse of the `ST_CONTAINS` function.', }), + preview: false, alias: undefined, signatures: [ { @@ -7275,6 +7347,7 @@ const stXDefinition: FunctionDefinition = { defaultMessage: 'Extracts the `x` coordinate from the supplied point.\nIf the points is of type `geo_point` this is equivalent to extracting the `longitude` value.', }), + preview: false, alias: undefined, signatures: [ { @@ -7314,6 +7387,7 @@ const stYDefinition: FunctionDefinition = { defaultMessage: 'Extracts the `y` coordinate from the supplied point.\nIf the points is of type `geo_point` this is equivalent to extracting the `latitude` value.', }), + preview: false, alias: undefined, signatures: [ { @@ -7353,6 +7427,7 @@ const startsWithDefinition: FunctionDefinition = { defaultMessage: 'Returns a boolean that indicates whether a keyword string starts with another string.', }), + preview: false, alias: undefined, signatures: [ { @@ -7430,6 +7505,7 @@ const substringDefinition: FunctionDefinition = { defaultMessage: 'Returns a substring of a string, specified by a start position and an optional length.', }), + preview: false, alias: undefined, signatures: [ { @@ -7490,6 +7566,7 @@ const tanDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.tan', { defaultMessage: 'Returns the tangent of an angle.', }), + preview: false, alias: undefined, signatures: [ { @@ -7546,6 +7623,7 @@ const tanhDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.tanh', { defaultMessage: 'Returns the hyperbolic tangent of a number.', }), + preview: false, alias: undefined, signatures: [ { @@ -7602,6 +7680,7 @@ const tauDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.tau', { defaultMessage: "Returns the ratio of a circle's circumference to its radius.", }), + preview: false, alias: undefined, signatures: [ { @@ -7622,6 +7701,7 @@ const toBase64Definition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_base64', { defaultMessage: 'Encode a string to a base64 string.', }), + preview: false, alias: undefined, signatures: [ { @@ -7659,6 +7739,7 @@ const toBooleanDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to a boolean value.\nA string value of *true* will be case-insensitive converted to the Boolean *true*.\nFor anything else, including the empty string, the function will return *false*.\nThe numerical value of *0* will be converted to *false*, anything else will be converted to *true*.', }), + preview: false, alias: ['to_bool'], signatures: [ { @@ -7749,6 +7830,7 @@ const toCartesianpointDefinition: FunctionDefinition = { 'Converts an input value to a `cartesian_point` value.\nA string will only be successfully converted if it respects WKT Point format.', } ), + preview: false, alias: undefined, signatures: [ { @@ -7801,6 +7883,7 @@ const toCartesianshapeDefinition: FunctionDefinition = { 'Converts an input value to a `cartesian_shape` value.\nA string will only be successfully converted if it respects WKT format.', } ), + preview: false, alias: undefined, signatures: [ { @@ -7859,6 +7942,7 @@ const toDateNanosDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_date_nanos', { defaultMessage: 'Converts an input to a nanosecond-resolution date value (aka date_nanos).', }), + preview: true, alias: undefined, signatures: [], supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], @@ -7874,6 +7958,7 @@ const toDateperiodDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_dateperiod', { defaultMessage: 'Converts an input value into a `date_period` value.', }), + preview: false, alias: undefined, signatures: [ { @@ -7923,6 +8008,7 @@ const toDatetimeDefinition: FunctionDefinition = { defaultMessage: "Converts an input value to a date value.\nA string will only be successfully converted if it's respecting the format `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`.\nTo convert dates in other formats, use `DATE_PARSE`.", }), + preview: false, alias: ['to_dt'], signatures: [ { @@ -8012,6 +8098,7 @@ const toDegreesDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_degrees', { defaultMessage: 'Converts a number in radians to degrees.', }), + preview: false, alias: undefined, signatures: [ { @@ -8069,6 +8156,7 @@ const toDoubleDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to a double value. If the input parameter is of a date type,\nits value will be interpreted as milliseconds since the Unix epoch,\nconverted to double. Boolean *true* will be converted to double *1.0*, *false* to *0.0*.', }), + preview: false, alias: ['to_dbl'], signatures: [ { @@ -8198,6 +8286,7 @@ const toGeopointDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to a `geo_point` value.\nA string will only be successfully converted if it respects WKT Point format.', }), + preview: false, alias: undefined, signatures: [ { @@ -8245,6 +8334,7 @@ const toGeoshapeDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to a `geo_shape` value.\nA string will only be successfully converted if it respects WKT format.', }), + preview: false, alias: undefined, signatures: [ { @@ -8304,6 +8394,7 @@ const toIntegerDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to an integer value.\nIf the input parameter is of a date type, its value will be interpreted as milliseconds\nsince the Unix epoch, converted to integer.\nBoolean *true* will be converted to integer *1*, *false* to *0*.', }), + preview: false, alias: ['to_int'], signatures: [ { @@ -8410,6 +8501,7 @@ const toIpDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_ip', { defaultMessage: 'Converts an input string to an IP value.', }), + preview: false, alias: undefined, signatures: [ { @@ -8459,6 +8551,7 @@ const toLongDefinition: FunctionDefinition = { defaultMessage: 'Converts an input value to a long value. If the input parameter is of a date type,\nits value will be interpreted as milliseconds since the Unix epoch, converted to long.\nBoolean *true* will be converted to long *1*, *false* to *0*.', }), + preview: false, alias: undefined, signatures: [ { @@ -8577,6 +8670,7 @@ const toLowerDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_lower', { defaultMessage: 'Returns a new string representing the input string converted to lower case.', }), + preview: false, alias: undefined, signatures: [ { @@ -8613,6 +8707,7 @@ const toRadiansDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_radians', { defaultMessage: 'Converts a number in degrees to radians.', }), + preview: false, alias: undefined, signatures: [ { @@ -8669,6 +8764,7 @@ const toStringDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_string', { defaultMessage: 'Converts an input value into a string.', }), + preview: false, alias: ['to_str'], signatures: [ { @@ -8825,6 +8921,7 @@ const toTimedurationDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_timeduration', { defaultMessage: 'Converts an input value into a `time_duration` value.', }), + preview: false, alias: undefined, signatures: [ { @@ -8877,6 +8974,7 @@ const toUnsignedLongDefinition: FunctionDefinition = { 'Converts an input value to an unsigned long value. If the input parameter is of a date type,\nits value will be interpreted as milliseconds since the Unix epoch, converted to unsigned long.\nBoolean *true* will be converted to unsigned long *1*, *false* to *0*.', } ), + preview: false, alias: ['to_ul', 'to_ulong'], signatures: [ { @@ -8975,6 +9073,7 @@ const toUpperDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_upper', { defaultMessage: 'Returns a new string representing the input string converted to upper case.', }), + preview: false, alias: undefined, signatures: [ { @@ -9011,6 +9110,7 @@ const toVersionDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.to_version', { defaultMessage: 'Converts an input string to a version value.', }), + preview: false, alias: ['to_ver'], signatures: [ { @@ -9057,6 +9157,7 @@ const trimDefinition: FunctionDefinition = { description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.trim', { defaultMessage: 'Removes leading and trailing whitespaces from a string.', }), + preview: false, alias: undefined, signatures: [ { @@ -9096,6 +9197,7 @@ const caseDefinition: FunctionDefinition = { defaultMessage: 'Accepts pairs of conditions and values. The function returns the value that belongs to the first condition that evaluates to `true`. If the number of arguments is odd, the last argument is the default value which is returned when no condition matches.', }), + preview: false, alias: undefined, signatures: [ { diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts index a86811f535f8..ba0a50c4a71b 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts @@ -118,6 +118,7 @@ export const isReturnType = (str: string | FunctionParameterType): str is Functi export interface FunctionDefinition { type: 'builtin' | 'agg' | 'eval'; + preview?: boolean; ignoreAsSuggestion?: boolean; name: string; alias?: string[]; From 8a7514f5e2478e8793e54a9de591286a0414ea60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Gonz=C3=A1lez?= Date: Tue, 12 Nov 2024 10:56:52 +0100 Subject: [PATCH 039/100] [Search][Connectors][a11y] Fixing wrong navigation sequence after closing Manual configuration dialog (#199613) ## Summary This PR closes this issue: https://github.com/elastic/kibana/issues/197623 Now we don't close the Popover content when clicking in any of the options listed in order to keep this content visible. And to solve the z-index issue where the Popover were displayed on top of the Flyout we are modifying the Flyout z-index to become exactly the same as the Popover + 1 to overlap this content. Now we keep the same tab index focus we got before opening the Flyout from the Popover options. ![CleanShot 2024-11-11 at 11 38 57](https://github.com/user-attachments/assets/0a1ff5cb-13ff-45ba-8a89-f5ca91ad77ef) --- .../create_connector/components/manual_configuration.tsx | 2 -- .../components/manual_configuration_flyout.tsx | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx index 825f47920d25..260486a3ec4c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration.tsx @@ -165,7 +165,6 @@ GET connector-${rawName}/_search onClick={() => { setFlyoutContent('manual_config'); setIsFlyoutVisible(true); - closePopover(); }} > {i18n.translate( @@ -206,7 +205,6 @@ GET connector-${rawName}/_search onClick={() => { setFlyoutContent('client'); setIsFlyoutVisible(true); - closePopover(); }} > {i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration_flyout.tsx index 98bea7ed62f7..03a633a5aa04 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/manual_configuration_flyout.tsx @@ -27,6 +27,7 @@ import { EuiSpacer, EuiText, EuiTitle, + useEuiTheme, useGeneratedHtmlId, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -68,13 +69,16 @@ export const ManualConfigurationFlyout: React.FC const { connectorName } = useValues(NewConnectorLogic); const { setRawName, createConnector } = useActions(NewConnectorLogic); - + const { euiTheme } = useEuiTheme(); return ( setIsFlyoutVisible(false)} aria-labelledby={simpleFlyoutTitleId} size="s" + // This fixes an a11y issue where the flyout was rendered below the Popover + // Now we let get the focus back to the Popover is we close the Flyout + maskProps={{ style: `z-index: ${Number(euiTheme.levels.menu) + 1}` }} > {flyoutContent === 'manual_config' && ( <> From fa6d8ee9e0f6fcaa0280fbf5ab60217f9f28279f Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 12 Nov 2024 10:02:26 +0000 Subject: [PATCH 040/100] [ML] File upload adding deployment initialization step (#198446) Fixes https://github.com/elastic/kibana/issues/196696 When adding a semantic text field, we now have an additional step in the file uploading process which calls inference for the selected inference endpoint. The response of the inference call is ignored and a poll is started to check to see of the model has been deployed by check to see if `num_allocations > 0` Any errors returned from the inference call will stop the upload, unless they are timeout errors which are ignored. https://github.com/user-attachments/assets/382ce565-3b4b-47a3-a081-d79c15aa462f --- .../file_data_visualizer_view.js | 3 - .../import_progress/import_progress.tsx | 136 +++++++++++++----- .../components/import_view/auto_deploy.ts | 76 ++++++++++ .../components/import_view/import.ts | 57 ++++++-- .../components/import_view/import_view.js | 26 +++- .../file_data_visualizer.tsx | 1 - .../plugins/data_visualizer/server/routes.ts | 50 ++++++- 7 files changed, 291 insertions(+), 58 deletions(-) create mode 100644 x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/auto_deploy.ts diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js index e19b2cbceda3..676830e94a28 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/file_data_visualizer_view/file_data_visualizer_view.js @@ -360,9 +360,6 @@ export class FileDataVisualizerView extends Component { fileName={fileName} fileContents={fileContents} data={data} - dataViewsContract={this.props.dataViewsContract} - dataStart={this.props.dataStart} - fileUpload={this.props.fileUpload} getAdditionalLinks={this.props.getAdditionalLinks} resultLinks={this.props.resultLinks} capabilities={this.props.capabilities} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_progress/import_progress.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_progress/import_progress.tsx index 53b92ab1f641..5a6db4f544fd 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_progress/import_progress.tsx +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_progress/import_progress.tsx @@ -31,6 +31,8 @@ export interface Statuses { createDataView: boolean; createPipeline: boolean; permissionCheckStatus: IMPORT_STATUS; + initializeDeployment: boolean; + initializeDeploymentStatus: IMPORT_STATUS; } export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { @@ -45,6 +47,8 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { uploadStatus, createDataView, createPipeline, + initializeDeployment, + initializeDeploymentStatus, } = statuses; let statusInfo = null; @@ -58,27 +62,37 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { ) { completedStep = 0; } + + if ( + readStatus === IMPORT_STATUS.COMPLETE && + initializeDeployment === true && + initializeDeploymentStatus === IMPORT_STATUS.INCOMPLETE + ) { + completedStep = 1; + } + if ( readStatus === IMPORT_STATUS.COMPLETE && + (initializeDeployment === false || initializeDeploymentStatus === IMPORT_STATUS.COMPLETE) && indexCreatedStatus === IMPORT_STATUS.INCOMPLETE && ingestPipelineCreatedStatus === IMPORT_STATUS.INCOMPLETE ) { - completedStep = 1; + completedStep = 2; } if (indexCreatedStatus === IMPORT_STATUS.COMPLETE) { - completedStep = 2; + completedStep = 3; } if ( ingestPipelineCreatedStatus === IMPORT_STATUS.COMPLETE || (createPipeline === false && indexCreatedStatus === IMPORT_STATUS.COMPLETE) ) { - completedStep = 3; + completedStep = 4; } if (uploadStatus === IMPORT_STATUS.COMPLETE) { - completedStep = 4; + completedStep = 5; } if (dataViewCreatedStatus === IMPORT_STATUS.COMPLETE) { - completedStep = 5; + completedStep = 6; } let processFileTitle = i18n.translate( @@ -87,6 +101,12 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { defaultMessage: 'Process file', } ); + let initializeDeploymentTitle = i18n.translate( + 'xpack.dataVisualizer.file.importProgress.initializeDeploymentTitle', + { + defaultMessage: 'Initialize model deployment', + } + ); let createIndexTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.createIndexTitle', { @@ -146,13 +166,43 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => {

); } - if (completedStep >= 1) { + if (initializeDeployment) { + if (completedStep >= 1) { + processFileTitle = i18n.translate( + 'xpack.dataVisualizer.file.importProgress.fileProcessedTitle', + { + defaultMessage: 'File processed', + } + ); + initializeDeploymentTitle = i18n.translate( + 'xpack.dataVisualizer.file.importProgress.initializingDeploymentTitle', + { + defaultMessage: 'Initializing model deployment', + } + ); + statusInfo = ( +

+ +

+ ); + } + } + if (completedStep >= 2) { processFileTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.fileProcessedTitle', { defaultMessage: 'File processed', } ); + initializeDeploymentTitle = i18n.translate( + 'xpack.dataVisualizer.file.importProgress.deploymentInitializedTitle', + { + defaultMessage: 'Model deployed', + } + ); createIndexTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.creatingIndexTitle', { @@ -162,7 +212,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { statusInfo = createPipeline === true ? creatingIndexAndIngestPipelineStatus : creatingIndexStatus; } - if (completedStep >= 2) { + if (completedStep >= 3) { createIndexTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.indexCreatedTitle', { @@ -178,7 +228,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { statusInfo = createPipeline === true ? creatingIndexAndIngestPipelineStatus : creatingIndexStatus; } - if (completedStep >= 3) { + if (completedStep >= 4) { createIngestPipelineTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.ingestPipelineCreatedTitle', { @@ -193,7 +243,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { ); statusInfo = ; } - if (completedStep >= 4) { + if (completedStep >= 5) { uploadingDataTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.dataUploadedTitle', { @@ -219,7 +269,7 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { statusInfo = null; } } - if (completedStep >= 5) { + if (completedStep >= 6) { createDataViewTitle = i18n.translate( 'xpack.dataVisualizer.file.importProgress.dataViewCreatedTitle', { @@ -239,44 +289,58 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { : 'selected') as EuiStepStatus, onClick: () => {}, }, - { - title: createIndexTitle, - status: (indexCreatedStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first - ? indexCreatedStatus + ]; + + if (initializeDeployment === true) { + steps.push({ + title: initializeDeploymentTitle, + status: (initializeDeploymentStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first + ? initializeDeploymentStatus : completedStep === 1 // Then show selected/incomplete states ? 'selected' : 'incomplete') as EuiStepStatus, onClick: () => {}, - }, - { - title: uploadingDataTitle, - status: (uploadStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first - ? uploadStatus - : completedStep === 3 // Then show selected/incomplete states - ? 'selected' - : 'incomplete') as EuiStepStatus, - onClick: () => {}, - }, - ]; + }); + } + + steps.push({ + title: createIndexTitle, + status: (indexCreatedStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first + ? indexCreatedStatus + : completedStep === 2 // Then show selected/incomplete states + ? 'selected' + : 'incomplete') as EuiStepStatus, + onClick: () => {}, + }); if (createPipeline === true) { - steps.splice(2, 0, { + steps.push({ title: createIngestPipelineTitle, status: (ingestPipelineCreatedStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first ? ingestPipelineCreatedStatus - : completedStep === 2 // Then show selected/incomplete states + : completedStep === 3 // Then show selected/incomplete states ? 'selected' : 'incomplete') as EuiStepStatus, onClick: () => {}, }); } + steps.push({ + title: uploadingDataTitle, + status: (uploadStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first + ? uploadStatus + : completedStep === 4 // Then show selected/incomplete states + ? 'selected' + : 'incomplete') as EuiStepStatus, + onClick: () => {}, + }); + if (createDataView === true) { steps.push({ title: createDataViewTitle, status: (dataViewCreatedStatus !== IMPORT_STATUS.INCOMPLETE // Show failure/completed states first ? dataViewCreatedStatus - : completedStep === 4 // Then show selected/incomplete states + : completedStep === 5 // Then show selected/incomplete states ? 'selected' : 'incomplete') as EuiStepStatus, onClick: () => {}, @@ -284,21 +348,21 @@ export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { } return ( - + <> {statusInfo && ( - + <> {statusInfo} - + )} - + ); }; const UploadFunctionProgress: FC<{ progress: number }> = ({ progress }) => { return ( - + <>

= ({ progress }) => { />

{progress < 100 && ( - + <> - + )} -
+ ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/auto_deploy.ts b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/auto_deploy.ts new file mode 100644 index 000000000000..a402d203585d --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/auto_deploy.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { InferenceInferenceEndpointInfo } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { HttpSetup } from '@kbn/core/public'; + +const POLL_INTERVAL = 5; // seconds + +export class AutoDeploy { + private inferError: Error | null = null; + constructor(private readonly http: HttpSetup, private readonly inferenceId: string) {} + + public async deploy() { + this.inferError = null; + if (await this.isDeployed()) { + return; + } + + this.infer().catch((e) => { + // ignore timeout errors + // The deployment may take a long time + // we'll know when it's ready from polling the inference endpoints + // looking for num_allocations + const status = e.response?.status; + if (status === 408 || status === 504 || status === 502) { + return; + } + this.inferError = e; + }); + await this.pollIsDeployed(); + } + + private async infer() { + return this.http.fetch( + `/internal/data_visualizer/inference/${this.inferenceId}`, + { + method: 'POST', + version: '1', + body: JSON.stringify({ input: '' }), + } + ); + } + + private async isDeployed() { + const inferenceEndpoints = await this.http.fetch( + '/internal/data_visualizer/inference_endpoints', + { + method: 'GET', + version: '1', + } + ); + return inferenceEndpoints.some((endpoint) => { + return ( + endpoint.inference_id === this.inferenceId && endpoint.service_settings.num_allocations > 0 + ); + }); + } + + private async pollIsDeployed() { + while (true) { + if (this.inferError !== null) { + throw this.inferError; + } + const isDeployed = await this.isDeployed(); + if (isDeployed) { + // break out of the loop once we have a successful deployment + return; + } + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL * 1000)); + } + } +} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import.ts b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import.ts index 8611e1ccd2ca..a44cb4fb890f 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import.ts +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import.ts @@ -12,13 +12,17 @@ import type { } from '@kbn/file-upload-plugin/common/types'; import type { FileUploadStartApi } from '@kbn/file-upload-plugin/public/api'; import { i18n } from '@kbn/i18n'; +import type { HttpSetup } from '@kbn/core/public'; +import type { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IMPORT_STATUS } from '../import_progress/import_progress'; +import { AutoDeploy } from './auto_deploy'; interface Props { data: ArrayBuffer; results: FindFileStructureResponse; dataViewsContract: DataViewsServicePublic; fileUpload: FileUploadStartApi; + http: HttpSetup; } interface Config { @@ -33,7 +37,7 @@ interface Config { } export async function importData(props: Props, config: Config, setState: (state: unknown) => void) { - const { data, results, dataViewsContract, fileUpload } = props; + const { data, results, dataViewsContract, fileUpload, http } = props; const { index, dataView, @@ -76,14 +80,6 @@ export async function importData(props: Props, config: Config, setState: (state: return; } - setState({ - importing: true, - imported: false, - reading: true, - initialized: true, - permissionCheckStatus: IMPORT_STATUS.COMPLETE, - }); - let success = true; let settings = {}; @@ -122,7 +118,15 @@ export async function importData(props: Props, config: Config, setState: (state: errors.push(`${parseError} ${error.message}`); } + const inferenceId = getInferenceId(mappings); + setState({ + importing: true, + imported: false, + reading: true, + initialized: true, + permissionCheckStatus: IMPORT_STATUS.COMPLETE, + initializeDeployment: inferenceId !== null, parseJSONStatus: getSuccess(success), }); @@ -147,6 +151,32 @@ export async function importData(props: Props, config: Config, setState: (state: return; } + if (inferenceId) { + // Initialize deployment + const autoDeploy = new AutoDeploy(http, inferenceId); + + try { + await autoDeploy.deploy(); + setState({ + initializeDeploymentStatus: IMPORT_STATUS.COMPLETE, + }); + } catch (error) { + success = false; + const deployError = i18n.translate('xpack.dataVisualizer.file.importView.deployModelError', { + defaultMessage: 'Error deploying trained model:', + }); + errors.push(`${deployError} ${error.message}`); + setState({ + initializeDeploymentStatus: IMPORT_STATUS.FAILED, + errors, + }); + } + } + + if (success === false) { + return; + } + const initializeImportResp = await importer.initializeImport(index, settings, mappings, pipeline); const timeFieldName = importer.getTimeField(); @@ -245,3 +275,12 @@ async function createKibanaDataView( function getSuccess(success: boolean) { return success ? IMPORT_STATUS.COMPLETE : IMPORT_STATUS.FAILED; } + +function getInferenceId(mappings: MappingTypeMapping) { + for (const value of Object.values(mappings.properties ?? {})) { + if (value.type === 'semantic_text') { + return value.inference_id; + } + } + return null; +} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js index 8481ed76e565..3f3006227006 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/components/import_view/import_view.js @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { debounce } from 'lodash'; +import { context } from '@kbn/kibana-react-plugin/public'; import { ResultsLinks } from '../../../common/components/results_links'; import { FilebeatConfigFlyout } from '../../../common/components/filebeat_config_flyout'; import { ImportProgress, IMPORT_STATUS } from '../import_progress'; @@ -76,14 +77,17 @@ const DEFAULT_STATE = { combinedFields: [], importer: undefined, createPipeline: true, + initializeDeployment: false, + initializeDeploymentStatus: IMPORT_STATUS.INCOMPLETE, }; export class ImportView extends Component { + static contextType = context; + constructor(props) { super(props); this.state = getDefaultState(DEFAULT_STATE, this.props.results, this.props.capabilities); - this.dataViewsContract = props.dataViewsContract; } componentDidMount() { @@ -98,7 +102,12 @@ export class ImportView extends Component { }; clickImport = () => { - const { data, results, dataViewsContract, fileUpload } = this.props; + const { data, results } = this.props; + const { + data: { dataViews: dataViewsContract }, + fileUpload, + http, + } = this.context.services; const { index, dataView, @@ -110,12 +119,13 @@ export class ImportView extends Component { } = this.state; const createPipeline = pipelineString !== ''; + this.setState({ createPipeline, }); importData( - { data, results, dataViewsContract, fileUpload }, + { data, results, dataViewsContract, fileUpload, http }, { index, dataView, @@ -150,7 +160,7 @@ export class ImportView extends Component { return; } - const exists = await this.props.fileUpload.checkIndexExists(index); + const exists = await this.context.services.fileUpload.checkIndexExists(index); const indexNameError = exists ? ( )} diff --git a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx index 01d9d2c37194..6289a73e2a66 100644 --- a/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/file_data_visualizer/file_data_visualizer.tsx @@ -44,7 +44,6 @@ export const FileDataVisualizer: FC = ({ getAdditionalLinks, resultLinks , logger: Logger) const filteredInferenceEndpoints = endpoints.filter((endpoint) => { return ( - (endpoint.task_type === 'sparse_embedding' || - endpoint.task_type === 'text_embedding') && - endpoint.service_settings.num_allocations >= 0 + endpoint.task_type === 'sparse_embedding' || endpoint.task_type === 'text_embedding' ); }); @@ -108,4 +106,50 @@ export function routes(coreSetup: CoreSetup, logger: Logger) } } ); + + /** + * @apiGroup DataVisualizer + * + * @api {get} /internal/data_visualizer/inference/{inferenceId} Runs inference on a given inference endpoint with the provided input + * @apiName inference + * @apiDescription Runs inference on a given inference endpoint with the provided input. + */ + router.versioned + .post({ + path: '/internal/data_visualizer/inference/{inferenceId}', + access: 'internal', + options: { + tags: ['access:fileUpload:analyzeFile'], + }, + }) + .addVersion( + { + version: '1', + validate: { + request: { + params: schema.object({ + inferenceId: schema.string(), + }), + body: schema.object({ + input: schema.string(), + }), + }, + }, + }, + async (context, request, response) => { + try { + const inferenceId = request.params.inferenceId; + const input = request.body.input; + const esClient = (await context.core).elasticsearch.client; + const body = await esClient.asCurrentUser.inference.inference({ + inference_id: inferenceId, + input, + }); + + return response.ok({ body }); + } catch (e) { + return response.customError(wrapError(e)); + } + } + ); } From 3d3b32faf6992f95805a37230e7e7e552e19a801 Mon Sep 17 00:00:00 2001 From: Nikita Indik Date: Tue, 12 Nov 2024 11:04:10 +0100 Subject: [PATCH 041/100] [Security Solution] `FinalEdit`: Add fields that are common for all rule types (#196642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Partially addresses: https://github.com/elastic/kibana/issues/171520** **Is a follow-up to: https://github.com/elastic/kibana/pull/196326** This PR enables editing of common fields in the new "Updates" tab of the rule upgrade flyout. The common fields are fields applicable to all rule types. ## Summary These fields are editable now: - `building_block` - `description` - `false_positives` - `investigation_fields` - `max_signals` - `note` - `references` - `related_integrations` - `required_fields` - `risk_score` - `risk_score_mapping` - `rule_name_override` - `rule_schedule` - `setup` - `severity` - `severity_mapping` - `tags` - `threat` - `timeline_template` - `timestamp_override` Scherm­afbeelding 2024-10-16 om 17 32 06 ### Testing - Ensure the `prebuiltRulesCustomizationEnabled` feature flag is enabled. - To simulate the availability of prebuilt rule upgrades, downgrade a currently installed prebuilt rule using the `PATCH api/detection_engine/rules` API. - Set `version: 1` in the request body to downgrade it to version 1. - Modify other rule fields in the request body as needed to test the changes. --- .../index.ts | 2 +- .../__snapshots__/index.test.tsx.snap | 0 .../disabled_types_with_tooltip_text.test.ts | 0 .../__tests__/index.test.tsx | 14 +- .../__tests__/use_es_field.test.ts} | 34 +-- .../disabled_types_with_tooltip_text.ts | 0 .../{field => es_field_selector}/index.tsx | 25 +- .../src/{field => es_field_selector}/types.ts | 9 - .../use_es_field.tsx} | 2 +- .../src/field_value_match/index.tsx | 8 +- .../components/builder/entry_renderer.tsx | 4 +- .../components/threat_match/entry_item.tsx | 6 +- .../make_validate_required_field.ts | 25 +- .../required_fields/required_fields.tsx | 8 +- .../required_fields/required_fields_row.tsx | 8 - .../components/required_fields/utils.ts | 25 ++ .../index.tsx | 34 +-- .../components/max_signals/index.tsx | 20 +- .../components/max_signals/translations.ts | 7 - .../risk_score_mapping/default_risk_score.tsx | 77 ++++++ .../components/risk_score_mapping/index.tsx | 210 +++------------- .../risk_score_override.tsx | 163 ++++++++++++ .../severity_mapping/default_severity.tsx | 60 +++++ .../components/severity_mapping/index.tsx | 233 ++--------------- .../severity_mapping/severity_override.tsx | 234 ++++++++++++++++++ .../components/severity_mapping/styles.ts | 24 ++ .../components/step_about_rule/index.tsx | 11 +- .../components/step_about_rule/schema.tsx | 31 ++- .../step_define_rule/index.test.tsx | 4 +- .../pages/rule_creation/helpers.ts | 25 +- .../default_risk_score_validator.ts | 30 +++ .../max_signals_validator_factory.ts | 26 ++ .../final_edit/common_rule_field_edit.tsx | 233 ++++++++++++++++- .../final_edit/fields/building_block.tsx | 32 +++ .../final_edit/fields/description.tsx | 27 ++ .../final_edit/fields/false_positives.tsx | 44 ++++ .../fields/investigation_fields.tsx | 82 ++++++ .../final_edit/fields/max_signals.tsx | 42 ++++ .../three_way_diff/final_edit/fields/name.tsx | 18 +- .../three_way_diff/final_edit/fields/note.tsx | 26 ++ .../final_edit/fields/references.tsx | 38 +++ .../fields/related_integrations.tsx | 39 +++ .../final_edit/fields/required_fields.tsx | 61 +++++ .../final_edit/fields/risk_score.tsx | 57 +++++ .../final_edit/fields/risk_score_mapping.tsx | 106 ++++++++ .../fields/rule_field_edit_form_wrapper.tsx | 4 +- .../final_edit/fields/rule_name_override.tsx | 71 ++++++ .../final_edit/fields/rule_schedule.tsx | 56 +++++ .../final_edit/fields/setup.tsx | 27 ++ .../final_edit/fields/severity.tsx | 25 ++ .../final_edit/fields/severity_mapping.tsx | 149 +++++++++++ .../three_way_diff/final_edit/fields/tags.tsx | 25 ++ .../final_edit/fields/threat.tsx | 30 +++ .../final_edit/fields/timeline_template.tsx | 48 ++++ .../final_edit/fields/timestamp_override.tsx | 95 +++++++ .../three_way_diff/final_edit/utils.ts | 41 +++ .../common_rule_field_readonly.tsx | 2 +- .../fields/building_block/building_block.tsx | 11 +- .../false_positives/false_positives.tsx | 4 + .../final_readonly/fields/note/note.tsx | 4 + .../fields/references/references.tsx | 4 + .../related_integrations.tsx | 4 + .../final_readonly/fields/setup/setup.tsx | 4 + .../final_readonly/fields/tags/tags.tsx | 4 + .../final_readonly/fields/threat/threat.tsx | 4 + .../pages/detection_engine/rules/utils.ts | 3 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 69 files changed, 2248 insertions(+), 534 deletions(-) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/__tests__/__snapshots__/index.test.tsx.snap (100%) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/__tests__/disabled_types_with_tooltip_text.test.ts (100%) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/__tests__/index.test.tsx (96%) rename packages/kbn-securitysolution-autocomplete/src/{field/__tests__/use_field.test.ts => es_field_selector/__tests__/use_es_field.test.ts} (91%) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/disabled_types_with_tooltip_text.ts (100%) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/index.tsx (85%) rename packages/kbn-securitysolution-autocomplete/src/{field => es_field_selector}/types.ts (85%) rename packages/kbn-securitysolution-autocomplete/src/{field/use_field.tsx => es_field_selector/use_es_field.tsx} (99%) rename x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/{autocomplete_field => es_field_selector_field}/index.tsx (70%) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/default_risk_score.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/risk_score_override.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/severity_override.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/styles.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/default_risk_score_validator.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/max_signals_validator_factory.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/building_block.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/description.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/false_positives.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/investigation_fields.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/max_signals.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/note.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/references.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/related_integrations.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/required_fields.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score_mapping.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_name_override.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/setup.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity_mapping.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/tags.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threat.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timeline_template.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timestamp_override.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/utils.ts diff --git a/packages/kbn-securitysolution-autocomplete/index.ts b/packages/kbn-securitysolution-autocomplete/index.ts index fef857773701..e47113719176 100644 --- a/packages/kbn-securitysolution-autocomplete/index.ts +++ b/packages/kbn-securitysolution-autocomplete/index.ts @@ -8,7 +8,7 @@ */ export * from './src/check_empty_value'; -export * from './src/field'; +export * from './src/es_field_selector'; export * from './src/field_value_exists'; export * from './src/field_value_lists'; export * from './src/field_value_match'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap similarity index 100% rename from packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/__snapshots__/index.test.tsx.snap diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/disabled_types_with_tooltip_text.test.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts similarity index 100% rename from packages/kbn-securitysolution-autocomplete/src/field/__tests__/disabled_types_with_tooltip_text.test.ts rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/disabled_types_with_tooltip_text.test.ts diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx similarity index 96% rename from packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx index b795abc5842f..1b33b4b29464 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/__tests__/index.test.tsx @@ -11,13 +11,13 @@ import React from 'react'; import { fireEvent, render, waitFor, within } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { FieldComponent } from '..'; +import { EsFieldSelector } from '..'; import { fields, getField } from '../../fields/index.mock'; describe('FieldComponent', () => { it('should render the component enabled and displays the selected field correctly', () => { const wrapper = render( - { }); it('should render the component disabled if isDisabled is true', () => { const wrapper = render( - { }); it('should render the loading spinner if isLoading is true when clicked', () => { const wrapper = render( - { }); it('should allow user to clear values if isClearable is true', () => { const wrapper = render( - { }); it('should change the selected value', async () => { const wrapper = render( - { it('it allows custom user input if "acceptsCustomOptions" is "true"', async () => { const mockOnChange = jest.fn(); const wrapper = render( - ({ BINARY_TYPE_NOT_SUPPORTED: 'Binary fields are currently unsupported', @@ -33,7 +33,7 @@ describe('useField', () => { describe('comboOptions and selectedComboOptions', () => { it('should return default values', () => { - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); const { isInvalid, comboOptions, selectedComboOptions, fieldWidth } = result.current; expect(isInvalid).toBeFalsy(); expect(comboOptions.length).toEqual(30); @@ -79,7 +79,7 @@ describe('useField', () => { }; const { result } = renderHook(() => - useField({ indexPattern: newIndexPattern, onChange: onChangeMock }) + useEsField({ indexPattern: newIndexPattern, onChange: onChangeMock }) ); const { comboOptions, selectedComboOptions } = result.current; expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]); @@ -124,7 +124,7 @@ describe('useField', () => { }; const { result } = renderHook(() => - useField({ + useEsField({ indexPattern: newIndexPattern, onChange: onChangeMock, selectedField: { name: '', type: 'keyword' }, @@ -173,7 +173,7 @@ describe('useField', () => { }; const { result } = renderHook(() => - useField({ + useEsField({ indexPattern: newIndexPattern, onChange: onChangeMock, selectedField: { name: ' ', type: 'keyword' }, @@ -222,7 +222,7 @@ describe('useField', () => { }; const { result } = renderHook(() => - useField({ indexPattern: newIndexPattern, onChange: onChangeMock, selectedField }) + useEsField({ indexPattern: newIndexPattern, onChange: onChangeMock, selectedField }) ); const { comboOptions, selectedComboOptions } = result.current; expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]); @@ -273,7 +273,7 @@ describe('useField', () => { readFromDocValues: true, }, ] as unknown as DataViewFieldBase[]; - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); const { comboOptions, renderFields } = result.current; expect(comboOptions).toEqual([ { label: 'blob' }, @@ -328,7 +328,7 @@ describe('useField', () => { readFromDocValues: true, }, ] as unknown as DataViewFieldBase[]; - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); const { comboOptions, renderFields } = result.current; expect(comboOptions).toEqual([ { label: 'blob' }, @@ -374,7 +374,7 @@ describe('useField', () => { readFromDocValues: true, }, ] as unknown as DataViewFieldBase[]; - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); const { comboOptions, renderFields } = result.current; expect(comboOptions).toEqual([{ label: 'bytes' }, { label: 'ssl' }, { label: '@timestamp' }]); act(() => { @@ -389,7 +389,7 @@ describe('useField', () => { jest.resetModules(); }); it('should invoke onChange with one value if one option is sent', () => { - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); act(() => { result.current.handleValuesChange([ { @@ -411,7 +411,7 @@ describe('useField', () => { }); }); it('should invoke onChange with array value if more than an option', () => { - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); act(() => { result.current.handleValuesChange([ { @@ -446,7 +446,7 @@ describe('useField', () => { }); }); it('should invoke onChange with custom option if one is sent', () => { - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); act(() => { result.current.handleCreateCustomOption('madeUpField'); expect(onChangeMock).toHaveBeenCalledWith([ @@ -462,13 +462,13 @@ describe('useField', () => { describe('fieldWidth', () => { it('should return object has width prop', () => { const { result } = renderHook(() => - useField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 100 }) + useEsField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 100 }) ); expect(result.current.fieldWidth).toEqual({ width: '100px' }); }); it('should return empty object', () => { const { result } = renderHook(() => - useField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 0 }) + useEsField({ indexPattern, onChange: onChangeMock, fieldInputWidth: 0 }) ); expect(result.current.fieldWidth).toEqual({}); }); @@ -477,7 +477,7 @@ describe('useField', () => { describe('isInvalid with handleTouch', () => { it('should return isInvalid equals true when calling with no selectedField and isRequired is true', () => { const { result } = renderHook(() => - useField({ indexPattern, onChange: onChangeMock, isRequired: true }) + useEsField({ indexPattern, onChange: onChangeMock, isRequired: true }) ); actTestRenderer(() => { @@ -487,7 +487,7 @@ describe('useField', () => { }); it('should return isInvalid equals false with selectedField and isRequired is true', () => { const { result } = renderHook(() => - useField({ indexPattern, onChange: onChangeMock, isRequired: true, selectedField }) + useEsField({ indexPattern, onChange: onChangeMock, isRequired: true, selectedField }) ); actTestRenderer(() => { @@ -496,7 +496,7 @@ describe('useField', () => { expect(result.current.isInvalid).toBeFalsy(); }); it('should return isInvalid equals false when isRequired is false', () => { - const { result } = renderHook(() => useField({ indexPattern, onChange: onChangeMock })); + const { result } = renderHook(() => useEsField({ indexPattern, onChange: onChangeMock })); actTestRenderer(() => { result.current.handleTouch(); diff --git a/packages/kbn-securitysolution-autocomplete/src/field/disabled_types_with_tooltip_text.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts similarity index 100% rename from packages/kbn-securitysolution-autocomplete/src/field/disabled_types_with_tooltip_text.ts rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/disabled_types_with_tooltip_text.ts diff --git a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx similarity index 85% rename from packages/kbn-securitysolution-autocomplete/src/field/index.tsx rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx index 7c830264af30..31efaa23b62d 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/index.tsx @@ -11,12 +11,22 @@ import React from 'react'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FieldProps } from './types'; -import { useField } from './use_field'; +import { FieldBaseProps } from './types'; +import { useEsField } from './use_es_field'; const AS_PLAIN_TEXT = { asPlainText: true }; -export const FieldComponent: React.FC = ({ +interface EsFieldSelectorProps extends FieldBaseProps { + isClearable?: boolean; + isDisabled?: boolean; + isLoading?: boolean; + placeholder: string; + acceptsCustomOptions?: boolean; + showMappingConflicts?: boolean; + 'aria-label'?: string; +} + +export function EsFieldSelector({ fieldInputWidth, fieldTypeFilter = [], indexPattern, @@ -30,18 +40,17 @@ export const FieldComponent: React.FC = ({ acceptsCustomOptions = false, showMappingConflicts = false, 'aria-label': ariaLabel, -}): JSX.Element => { +}: EsFieldSelectorProps): JSX.Element { const { isInvalid, comboOptions, selectedComboOptions, fieldWidth, - renderFields, handleTouch, handleValuesChange, handleCreateCustomOption, - } = useField({ + } = useEsField({ indexPattern, fieldTypeFilter, isRequired, @@ -97,6 +106,4 @@ export const FieldComponent: React.FC = ({ aria-label={ariaLabel} /> ); -}; - -FieldComponent.displayName = 'Field'; +} diff --git a/packages/kbn-securitysolution-autocomplete/src/field/types.ts b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts similarity index 85% rename from packages/kbn-securitysolution-autocomplete/src/field/types.ts rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts index 26e0eb969770..b0f1ab56e807 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/types.ts +++ b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/types.ts @@ -11,15 +11,6 @@ import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import { FieldConflictsInfo } from '@kbn/securitysolution-list-utils'; import { GetGenericComboBoxPropsReturn } from '../get_generic_combo_box_props'; -export interface FieldProps extends FieldBaseProps { - isClearable: boolean; - isDisabled: boolean; - isLoading: boolean; - placeholder: string; - acceptsCustomOptions?: boolean; - showMappingConflicts?: boolean; - 'aria-label'?: string; -} export interface FieldBaseProps { indexPattern: DataViewBase | undefined; fieldTypeFilter?: string[]; diff --git a/packages/kbn-securitysolution-autocomplete/src/field/use_field.tsx b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx similarity index 99% rename from packages/kbn-securitysolution-autocomplete/src/field/use_field.tsx rename to packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx index 7a0b7eb5b00a..615571d98960 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/use_field.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/es_field_selector/use_es_field.tsx @@ -115,7 +115,7 @@ const getComboBoxProps = (fields: ComboBoxFields): GetFieldComboBoxPropsReturn = }; }; -export const useField = ({ +export const useEsField = ({ indexPattern, fieldTypeFilter, isRequired, diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx index 6a831376b3cb..5b627451db19 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx @@ -48,9 +48,9 @@ interface AutocompleteFieldMatchProps { selectedField: DataViewFieldBase | undefined; selectedValue: string | undefined; indexPattern: DataViewBase | undefined; - isLoading: boolean; - isDisabled: boolean; - isClearable: boolean; + isLoading?: boolean; + isDisabled?: boolean; + isClearable?: boolean; isRequired?: boolean; fieldInputWidth?: number; rowLabel?: string; @@ -68,7 +68,7 @@ export const AutocompleteFieldMatchComponent: React.FC = ({ (isFirst: boolean): JSX.Element => { const filteredIndexPatterns = getFilteredIndexPatterns(indexPattern, entry); const comboBox = ( - = ({ const renderFieldInput = useMemo(() => { const comboBox = ( - = ({ const renderThreatFieldInput = useMemo(() => { const comboBox = ( - > | undefined { const [{ value, path, form }] = args; - const formData = form.getFormData(); - const parentFieldData: RequiredFieldInput[] = formData[parentFieldPath]; + const allRequiredFields = getAllRequiredFieldsValues(form, parentFieldPath); const isFieldNameUsedMoreThanOnce = - parentFieldData.filter((field) => field.name === value.name).length > 1; + allRequiredFields.filter((field) => field.name === value.name).length > 1; if (isFieldNameUsedMoreThanOnce) { return { @@ -51,3 +51,20 @@ export function makeValidateRequiredField(parentFieldPath: string) { } }; } + +function getAllRequiredFieldsValues( + form: { getFields: FormHook['getFields'] }, + parentFieldPath: string +) { + /* + Getting values for required fields via flattened fields instead of using `getFormData`. + This is because `getFormData` applies a serializer function to field values, which might update values. + Using flattened fields allows us to get the original values before the serializer function is applied. + */ + const flattenedFieldNames = getFlattenedArrayFieldNames(form, parentFieldPath); + const fields = form.getFields(); + + return flattenedFieldNames.map( + (fieldName) => fields[fieldName]?.value ?? {} + ) as RequiredFieldInput[]; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx index f53c41ce98d0..f2f4c08a3581 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx @@ -16,6 +16,7 @@ import { RequiredFieldsHelpInfo } from './required_fields_help_info'; import { RequiredFieldRow } from './required_fields_row'; import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations'; import * as i18n from './translations'; +import { getFlattenedArrayFieldNames } from './utils'; interface RequiredFieldsComponentProps { path: string; @@ -65,7 +66,7 @@ const RequiredFieldsList = ({ form, }: RequiredFieldsListProps) => { /* - This component should only re-render when either the "index" form field (index patterns) or the required fields change. + This component should only re-render when either the "index" form field (index patterns) or the required fields change. By default, the `useFormData` hook triggers a re-render whenever any form field changes. It also allows optimization by passing a "watch" array of field names. The component then only re-renders when these specified fields change. @@ -77,10 +78,7 @@ const RequiredFieldsList = ({ To work around this, we manually construct a list of "flattened" field names to watch, based on the current state of the form. This is a temporary solution and ideally, `useFormData` should be updated to handle this scenario. */ - - const internalField = form.getFields()[`${path}__array__`] ?? {}; - const internalFieldValue = (internalField?.value ?? []) as ArrayItem[]; - const flattenedFieldNames = internalFieldValue.map((item) => item.path); + const flattenedFieldNames = getFlattenedArrayFieldNames(form, path); /* Not using "watch" for the initial render, to let row components render and initialize form fields. diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx index 755f1de41376..39b08ac17e49 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx @@ -44,14 +44,6 @@ export const RequiredFieldRow = ({ const rowFieldConfig: FieldConfig = useMemo( () => ({ - deserializer: (value) => { - const rowValueWithoutEcs: RequiredFieldInput = { - name: value.name, - type: value.type, - }; - - return rowValueWithoutEcs; - }, validations: [{ validator: makeValidateRequiredField(parentFieldPath) }], defaultValue: { name: '', type: '' }, }), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts index 55beca264e12..38820e992fa6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts @@ -5,6 +5,8 @@ * 2.0. */ +import type { FormHook, ArrayItem } from '../../../../shared_imports'; + interface PickTypeForNameParameters { name: string; type: string; @@ -26,3 +28,26 @@ export function pickTypeForName({ name, type, typesByFieldName = {} }: PickTypeF */ return typesAvailableForName[0] ?? type; } + +/** + * Returns a list of flattened field names for a given array field of a form. + * Flattened field name is a string that represents the path to an item in an array field. + * For example, a field "myArrayField" can be represented as "myArrayField[0]", "myArrayField[1]", etc. + * + * Flattened field names are useful: + * - when you need to subscribe to changes in an array field using `useFormData` "watch" option + * - when you need to retrieve form data before serializer function is applied + * + * @param {Object} form - Form object. + * @param {string} arrayFieldName - Path to the array field. + * @returns {string[]} - Flattened array field names. + */ +export function getFlattenedArrayFieldNames( + form: { getFields: FormHook['getFields'] }, + arrayFieldName: string +): string[] { + const internalField = form.getFields()[`${arrayFieldName}__array__`] ?? {}; + const internalFieldValue = (internalField?.value ?? []) as ArrayItem[]; + + return internalFieldValue.map((item) => item.path); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/autocomplete_field/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/es_field_selector_field/index.tsx similarity index 70% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/autocomplete_field/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/es_field_selector_field/index.tsx index 37c19f5724ec..03d0a3021dc3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/autocomplete_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/es_field_selector_field/index.tsx @@ -7,13 +7,13 @@ import React, { useCallback, useMemo } from 'react'; import { EuiFormRow } from '@elastic/eui'; -import { FieldComponent } from '@kbn/securitysolution-autocomplete'; +import { EsFieldSelector } from '@kbn/securitysolution-autocomplete'; import type { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -interface AutocompleteFieldProps { +interface EsFieldSelectorFieldProps { dataTestSubj: string; - field: FieldHook; + field: FieldHook; idAria: string; indices: DataViewBase; isDisabled: boolean; @@ -21,7 +21,7 @@ interface AutocompleteFieldProps { placeholder?: string; } -export const AutocompleteField = ({ +export const EsFieldSelectorField = ({ dataTestSubj, field, idAria, @@ -29,35 +29,37 @@ export const AutocompleteField = ({ isDisabled, fieldType, placeholder, -}: AutocompleteFieldProps) => { +}: EsFieldSelectorFieldProps) => { + const fieldTypeFilter = useMemo(() => [fieldType], [fieldType]); + const handleFieldChange = useCallback( ([newField]: DataViewFieldBase[]): void => { - // TODO: Update onChange type in FieldComponent as newField can be undefined field.setValue(newField?.name ?? ''); }, [field] ); - const selectedField = useMemo(() => { - const existingField = (field.value as string) ?? ''; - const [newSelectedField] = indices.fields.filter( - ({ name }) => existingField != null && existingField === name - ); - return newSelectedField; - }, [field.value, indices]); + const selectedField = useMemo( + () => + indices.fields.find(({ name }) => field.value === name) ?? { + name: field.value, + type: fieldType, + }, + [field.value, indices, fieldType] + ); - const fieldTypeFilter = useMemo(() => [fieldType], [fieldType]); + const describedByIds = useMemo(() => (idAria ? [idAria] : undefined), [idAria]); return ( - = ({ const { alerting } = useKibana().services; const maxAlertsPerRun = alerting.getMaxAlertsPerRun(); - const [isInvalid, error] = useMemo(() => { - if (typeof value === 'number' && !isNaN(value) && value <= 0) { - return [true, i18n.GREATER_THAN_ERROR]; - } - return [false]; - }, [value]); + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); const hasWarning = useMemo( () => typeof value === 'number' && !isNaN(value) && value > maxAlertsPerRun, @@ -67,6 +66,8 @@ export const MaxSignals: React.FC = ({ return textToRender; }, [hasWarning, maxAlertsPerRun]); + const describedByIds = useMemo(() => (idAria ? [idAria] : undefined), [idAria]); + return ( = ({ width: ${MAX_SIGNALS_FIELD_WIDTH}px; } `} - describedByIds={idAria ? [idAria] : undefined} + describedByIds={describedByIds} fullWidth helpText={helpText} label={field.label} labelAppend={field.labelAppend} isInvalid={isInvalid} - error={error} + error={errorMessage} > = ({ data-test-subj={dataTestSubj} disabled={isDisabled} append={hasWarning ? : undefined} + min={MIN_VALUE} /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/max_signals/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/max_signals/translations.ts index b69c0557a051..b5b135d456f3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/max_signals/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/max_signals/translations.ts @@ -7,13 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const GREATER_THAN_ERROR = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.maxAlertsFieldGreaterThanError', - { - defaultMessage: 'Max alerts must be greater than 0.', - } -); - export const LESS_THAN_WARNING = (maxNumber: number) => i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.maxAlertsFieldLessThanWarning', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/default_risk_score.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/default_risk_score.tsx new file mode 100644 index 000000000000..94899f35e272 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/default_risk_score.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import { EuiFormRow, EuiText, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiRange } from '@elastic/eui'; +import type { EuiRangeProps } from '@elastic/eui'; +import { MAX_RISK_SCORE, MIN_RISK_SCORE } from '../../validators/default_risk_score_validator'; +import * as i18n from './translations'; + +interface DefaultRiskScoreProps { + value: number; + onChange: (newValue: number) => void; + errorMessage?: string; + idAria?: string; + dataTestSubj?: string; +} + +export function DefaultRiskScore({ + value, + onChange, + errorMessage, + idAria, + dataTestSubj = 'defaultRiskScore', +}: DefaultRiskScoreProps) { + const handleChange = useCallback>( + (event) => { + const eventValue = (event.target as HTMLInputElement).value; + const intOrNanValue = Number.parseInt(eventValue.trim(), 10); + const intValue = Number.isNaN(intOrNanValue) ? MIN_RISK_SCORE : intOrNanValue; + + onChange(intValue); + }, + [onChange] + ); + + return ( + + } + error={errorMessage} + isInvalid={!!errorMessage} + fullWidth + data-test-subj={`${dataTestSubj}-defaultRisk`} + describedByIds={idAria ? [idAria] : undefined} + > + + + + ); +} + +function DefaultRiskScoreLabel() { + return ( +
+ + {i18n.DEFAULT_RISK_SCORE} + + + {i18n.RISK_SCORE_DESCRIPTION} +
+ ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/index.tsx index 1cd775c2e80e..d5360bbf0400 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/index.tsx @@ -5,45 +5,16 @@ * 2.0. */ -import React, { useCallback, useMemo } from 'react'; -import styled from 'styled-components'; -import { noop } from 'lodash/fp'; -import { - EuiFormRow, - EuiCheckbox, - EuiText, - EuiFlexGroup, - EuiFlexItem, - EuiFormLabel, - EuiIcon, - EuiSpacer, - EuiRange, -} from '@elastic/eui'; -import type { EuiRangeProps } from '@elastic/eui'; - +import React, { useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; -import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { FieldComponent } from '@kbn/securitysolution-autocomplete'; -import type { RiskScoreMapping } from '@kbn/securitysolution-io-ts-alerting-types'; - +import { + getFieldValidityAndErrorMessage, + type FieldHook, +} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import type { AboutStepRiskScore } from '../../../../detections/pages/detection_engine/rules/types'; -import * as i18n from './translations'; - -const NestedContent = styled.div` - margin-left: 24px; -`; - -const EuiFlexItemComboBoxColumn = styled(EuiFlexItem)` - max-width: 376px; -`; - -const EuiFlexItemIconColumn = styled(EuiFlexItem)` - width: 20px; -`; - -const EuiFlexItemRiskScoreColumn = styled(EuiFlexItem)` - width: 160px; -`; +import { DefaultRiskScore } from './default_risk_score'; +import { RiskScoreOverride } from './risk_score_override'; interface RiskScoreFieldProps { dataTestSubj: string; @@ -51,7 +22,6 @@ interface RiskScoreFieldProps { idAria: string; indices: DataViewBase; isDisabled: boolean; - placeholder?: string; } export const RiskScoreField = ({ @@ -60,19 +30,14 @@ export const RiskScoreField = ({ idAria, indices, isDisabled, - placeholder, }: RiskScoreFieldProps) => { const { value, isMappingChecked, mapping } = field.value; const { setValue } = field; - const fieldTypeFilter = useMemo(() => ['number'], []); - const selectedField = useMemo(() => getFieldTypeByMapping(mapping, indices), [mapping, indices]); - - const handleDefaultRiskScoreChange = useCallback>( - (e) => { - const range = (e.target as HTMLInputElement).value; + const handleDefaultRiskScoreChange = useCallback( + (newDefaultRiskScoreValue: number) => { setValue({ - value: Number(range.trim()), + value: newDefaultRiskScoreValue, isMappingChecked, mapping, }); @@ -106,146 +71,29 @@ export const RiskScoreField = ({ }); }, [setValue, value, isMappingChecked, mapping]); - const riskScoreLabel = useMemo(() => { - return ( -
- - {i18n.DEFAULT_RISK_SCORE} - - - {i18n.RISK_SCORE_DESCRIPTION} -
- ); - }, []); - - const riskScoreMappingLabel = useMemo(() => { - return ( -
- - - - - {i18n.RISK_SCORE_MAPPING} - - - - {i18n.RISK_SCORE_MAPPING_DESCRIPTION} - -
- ); - }, [isMappingChecked, handleRiskScoreMappingChecked, isDisabled]); + const errorMessage = getFieldValidityAndErrorMessage(field).errorMessage ?? undefined; return ( + - - - - - - {i18n.RISK_SCORE_MAPPING_DETAILS} : '' - } - error={'errorMessage'} - isInvalid={false} - fullWidth - data-test-subj={`${dataTestSubj}-riskOverride`} - describedByIds={idAria ? [idAria] : undefined} - > - - - {isMappingChecked && ( - - - - - {i18n.SOURCE_FIELD} - - - - {i18n.DEFAULT_RISK_SCORE} - - - - - - - - - - - - - - {i18n.RISK_SCORE_FIELD} - - - - - )} - - + ); }; - -/** - * Looks for field metadata (DataViewFieldBase) in existing index pattern. - * If specified field doesn't exist, returns a stub DataViewFieldBase created based on the mapping -- - * because the field might not have been indexed yet, but we still need to display the mapping. - * - * @param mapping Mapping of a specified field name to risk score. - * @param pattern Existing index pattern. - */ -const getFieldTypeByMapping = ( - mapping: RiskScoreMapping, - pattern: DataViewBase -): DataViewFieldBase => { - const field = mapping?.[0]?.field ?? ''; - const [knownFieldType] = pattern.fields.filter(({ name }) => field != null && field === name); - return knownFieldType ?? { name: field, type: 'number' }; -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/risk_score_override.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/risk_score_override.tsx new file mode 100644 index 000000000000..d6067062685f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/risk_score_mapping/risk_score_override.tsx @@ -0,0 +1,163 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { noop } from 'lodash/fp'; +import styled from 'styled-components'; +import { + EuiFormRow, + EuiCheckbox, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiIcon, + EuiSpacer, +} from '@elastic/eui'; +import type { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; +import { EsFieldSelector } from '@kbn/securitysolution-autocomplete'; +import * as i18n from './translations'; +import type { RiskScoreMapping } from '../../../../../common/api/detection_engine'; + +const NestedContent = styled.div` + margin-left: 24px; +`; + +const EuiFlexItemComboBoxColumn = styled(EuiFlexItem)` + max-width: 376px; +`; + +const EuiFlexItemIconColumn = styled(EuiFlexItem)` + width: 20px; +`; + +const EuiFlexItemRiskScoreColumn = styled(EuiFlexItem)` + width: 160px; +`; + +const fieldTypeFilter = ['number']; + +interface RiskScoreOverrideProps { + isMappingChecked: boolean; + onToggleMappingChecked: () => void; + onMappingChange: ([newField]: DataViewFieldBase[]) => void; + mapping: RiskScoreMapping; + indices: DataViewBase; + dataTestSubj?: string; + idAria?: string; + isDisabled: boolean; +} + +export function RiskScoreOverride({ + isMappingChecked, + onToggleMappingChecked, + onMappingChange, + mapping, + indices, + dataTestSubj = 'riskScoreOverride', + idAria, + isDisabled, +}: RiskScoreOverrideProps) { + const riskScoreMappingLabel = useMemo(() => { + return ( +
+ + + + + {i18n.RISK_SCORE_MAPPING} + + + + {i18n.RISK_SCORE_MAPPING_DESCRIPTION} + +
+ ); + }, [isDisabled, isMappingChecked, onToggleMappingChecked]); + + const describedByIds = useMemo(() => (idAria ? [idAria] : undefined), [idAria]); + const selectedField = useMemo(() => getFieldTypeByMapping(mapping, indices), [mapping, indices]); + + return ( + {i18n.RISK_SCORE_MAPPING_DETAILS} : '' + } + fullWidth + data-test-subj={`${dataTestSubj}-riskOverride`} + describedByIds={describedByIds} + > + + + {isMappingChecked && ( + + + + + {i18n.SOURCE_FIELD} + + + + {i18n.DEFAULT_RISK_SCORE} + + + + + + + + + + + + + + {i18n.RISK_SCORE_FIELD} + + + + + )} + + + ); +} + +/** + * Looks for field metadata (DataViewFieldBase) in existing index pattern. + * If specified field doesn't exist, returns a stub DataViewFieldBase created based on the mapping -- + * because the field might not have been indexed yet, but we still need to display the mapping. + * + * @param mapping Mapping of a specified field name to risk score. + * @param pattern Existing index pattern. + */ +const getFieldTypeByMapping = ( + mapping: RiskScoreMapping, + pattern: DataViewBase +): DataViewFieldBase => { + const field = mapping?.[0]?.field ?? ''; + const [knownFieldType] = pattern.fields.filter(({ name }) => field != null && field === name); + return knownFieldType ?? { name: field, type: 'number' }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx new file mode 100644 index 000000000000..477b4bcd0f51 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/default_severity.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiFormRow, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiSuperSelect, +} from '@elastic/eui'; +import React from 'react'; +import type { Severity } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import { severityOptions } from '../step_about_rule/data'; +import * as i18n from './translations'; + +const describedByIds = ['detectionEngineStepAboutRuleSeverity']; + +interface DefaultSeverityProps { + value: Severity; + onChange: (newValue: Severity) => void; +} + +export function DefaultSeverity({ value, onChange }: DefaultSeverityProps) { + return ( + + } + fullWidth + data-test-subj="detectionEngineStepAboutRuleSeverity" + describedByIds={describedByIds} + > + + + + ); +} + +function DefaultSeverityLabel() { + return ( +
+ + {i18n.SEVERITY} + + + {i18n.SEVERITY_DESCRIPTION} +
+ ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/index.tsx index 425564e1ed5f..28d0a2c62241 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/index.tsx @@ -5,53 +5,14 @@ * 2.0. */ -import { - EuiFormRow, - EuiCheckbox, - EuiText, - EuiFlexGroup, - EuiFlexItem, - EuiFormLabel, - EuiIcon, - EuiSpacer, - EuiSuperSelect, -} from '@elastic/eui'; -import { noop } from 'lodash/fp'; -import React, { useCallback, useMemo } from 'react'; -import styled from 'styled-components'; - +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useCallback } from 'react'; import type { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; -import { - FieldComponent, - AutocompleteFieldMatchComponent, -} from '@kbn/securitysolution-autocomplete'; -import type { - Severity, - SeverityMapping, - SeverityMappingItem, -} from '@kbn/securitysolution-io-ts-alerting-types'; - -import type { SeverityOptionItem } from '../step_about_rule/data'; +import type { Severity, SeverityMapping } from '@kbn/securitysolution-io-ts-alerting-types'; import type { AboutStepSeverity } from '../../../../detections/pages/detection_engine/rules/types'; -import { useKibana } from '../../../../common/lib/kibana'; -import * as i18n from './translations'; - -const NestedContent = styled.div` - margin-left: 24px; -`; - -const EuiFlexItemComboBoxColumn = styled(EuiFlexItem)` - max-width: 376px; -`; - -const EuiFlexItemIconColumn = styled(EuiFlexItem)` - width: 20px; -`; - -const EuiFlexItemSeverityColumn = styled(EuiFlexItem)` - width: 80px; -`; +import { DefaultSeverity } from './default_severity'; +import { SeverityOverride } from './severity_override'; interface SeverityFieldProps { dataTestSubj: string; @@ -59,7 +20,6 @@ interface SeverityFieldProps { idAria: string; indices: DataViewBase; isDisabled: boolean; - options: SeverityOptionItem[]; setRiskScore: (severity: Severity) => void; } @@ -69,10 +29,8 @@ export const SeverityField = ({ idAria, indices, isDisabled, - options, setRiskScore, }: SeverityFieldProps) => { - const { services } = useKibana(); const { value, isMappingChecked, mapping } = field.value; const { setValue } = field; @@ -126,6 +84,7 @@ export const SeverityField = ({ severity, }, ]; + handleFieldValueChange(newMappingItems, index); }, [mapping, handleFieldValueChange] @@ -139,178 +98,22 @@ export const SeverityField = ({ }); }, [isMappingChecked, mapping, value, setValue]); - const severityLabel = useMemo(() => { - return ( -
- - {i18n.SEVERITY} - - - {i18n.SEVERITY_DESCRIPTION} -
- ); - }, []); - - const severityMappingLabel = useMemo(() => { - return ( -
- - - - - {i18n.SEVERITY_MAPPING} - - - - {i18n.SEVERITY_MAPPING_DESCRIPTION} - -
- ); - }, [handleSeverityMappingChecked, isDisabled, isMappingChecked]); - return ( + - - - - - - - {i18n.SEVERITY_MAPPING_DETAILS} : '' - } - error={'errorMessage'} - isInvalid={false} - fullWidth - data-test-subj={`${dataTestSubj}-severityOverride`} - describedByIds={idAria ? [idAria] : undefined} - > - - - {isMappingChecked && ( - - - - - {i18n.SOURCE_FIELD} - - - {i18n.SOURCE_VALUE} - - - - {i18n.DEFAULT_SEVERITY} - - - - - {mapping.map((severityMappingItem: SeverityMappingItem, index) => ( - - - - - - - - - - - - - - { - options.find((o) => o.value === severityMappingItem.severity) - ?.inputDisplay - } - - - - ))} - - )} - - + ); }; - -/** - * Looks for field metadata (DataViewFieldBase) in existing index pattern. - * If specified field doesn't exist, returns a stub DataViewFieldBase created based on the mapping -- - * because the field might not have been indexed yet, but we still need to display the mapping. - * - * @param mapping Mapping of a specified field name + value to a certain severity value. - * @param pattern Existing index pattern. - */ -const getFieldTypeByMapping = ( - mapping: SeverityMappingItem, - pattern: DataViewBase -): DataViewFieldBase => { - const { field } = mapping; - const [knownFieldType] = pattern.fields.filter(({ name }) => field === name); - return knownFieldType ?? { name: field, type: 'string' }; -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/severity_override.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/severity_override.tsx new file mode 100644 index 000000000000..046f360c3af4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/severity_override.tsx @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiFormRow, + EuiCheckbox, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiIcon, + EuiSpacer, +} from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import type { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; +import { + EsFieldSelector, + AutocompleteFieldMatchComponent, +} from '@kbn/securitysolution-autocomplete'; +import type { Severity, SeverityMappingItem } from '@kbn/securitysolution-io-ts-alerting-types'; +import { severityOptions } from '../step_about_rule/data'; +import { useKibana } from '../../../../common/lib/kibana'; +import * as styles from './styles'; +import * as i18n from './translations'; + +interface SeverityOverrideProps { + isDisabled: boolean; + onSeverityMappingChecked: () => void; + onFieldChange: (index: number, severity: Severity, [newField]: DataViewFieldBase[]) => void; + onFieldMatchValueChange: (index: number, severity: Severity, newMatchValue: string) => void; + isMappingChecked: boolean; + dataTestSubj?: string; + idAria?: string; + mapping: SeverityMappingItem[]; + indices: DataViewBase; +} + +export function SeverityOverride({ + isDisabled, + onSeverityMappingChecked, + onFieldChange, + onFieldMatchValueChange, + isMappingChecked, + dataTestSubj = 'severity', + idAria, + mapping, + indices, +}: SeverityOverrideProps) { + const severityMappingLabel = useMemo(() => { + return ( +
+ + + + + {i18n.SEVERITY_MAPPING} + + + + {i18n.SEVERITY_MAPPING_DESCRIPTION} + +
+ ); + }, [onSeverityMappingChecked, isDisabled, isMappingChecked]); + + const describedByIds = useMemo(() => (idAria ? [idAria] : undefined), [idAria]); + + return ( + {i18n.SEVERITY_MAPPING_DETAILS} : '' + } + fullWidth + data-test-subj={`${dataTestSubj}-severityOverride`} + describedByIds={describedByIds} + > + + + {isMappingChecked && ( + + + + + {i18n.SOURCE_FIELD} + + + {i18n.SOURCE_VALUE} + + + + {i18n.DEFAULT_SEVERITY} + + + + + {mapping.map((severityMappingItem, index) => ( + + ))} + + )} + + + ); +} + +interface SeverityMappingRowProps { + severityMappingItem: SeverityMappingItem; + index: number; + indices: DataViewBase; + isDisabled: boolean; + onFieldChange: (index: number, severity: Severity, [newField]: DataViewFieldBase[]) => void; + onFieldMatchValueChange: (index: number, severity: Severity, newMatchValue: string) => void; +} + +function SeverityMappingRow({ + severityMappingItem, + index, + indices, + isDisabled, + onFieldChange, + onFieldMatchValueChange, +}: SeverityMappingRowProps) { + const { services } = useKibana(); + + const handleFieldChange = useCallback( + (newField: DataViewFieldBase[]) => { + onFieldChange(index, severityMappingItem.severity, newField); + }, + [index, severityMappingItem.severity, onFieldChange] + ); + + const handleFieldMatchValueChange = useCallback( + (newMatchValue: string) => { + onFieldMatchValueChange(index, severityMappingItem.severity, newMatchValue); + }, + [index, severityMappingItem.severity, onFieldMatchValueChange] + ); + + return ( + + + + + + + + + + + + + + {severityOptions.find((o) => o.value === severityMappingItem.severity)?.inputDisplay} + + + + ); +} + +const NestedContent: React.FC = ({ children }) => ( +
{children}
+); + +const EuiFlexItemComboBoxColumn: React.FC = ({ children }) => ( + {children} +); + +const EuiFlexItemIconColumn: React.FC = ({ children }) => ( + + {children} + +); + +const EuiFlexItemSeverityColumn: React.FC = ({ children }) => ( + + {children} + +); + +/** + * Looks for field metadata (DataViewFieldBase) in existing index pattern. + * If specified field doesn't exist, returns a stub DataViewFieldBase created based on the mapping -- + * because the field might not have been indexed yet, but we still need to display the mapping. + * + * @param mapping Mapping of a specified field name + value to a certain severity value. + * @param pattern Existing index pattern. + */ +const getFieldTypeByMapping = ( + mapping: SeverityMappingItem, + pattern: DataViewBase +): DataViewFieldBase => { + const { field } = mapping; + const [knownFieldType] = pattern.fields.filter(({ name }) => field === name); + return knownFieldType ?? { name: field, type: 'string' }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/styles.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/styles.ts new file mode 100644 index 000000000000..480a442b250d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/severity_mapping/styles.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/css'; + +export const nestedContent = css` + margin-left: 24px; +`; + +export const comboBoxColumn = css` + max-width: 376px; +`; + +export const iconColumn = css` + width: 20px; +`; + +export const severityColumn = css` + width: 80px; +`; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx index 7666a9ba8aee..ac5c91aa8a25 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.tsx @@ -24,7 +24,7 @@ import { AddMitreAttackThreat } from '../mitre'; import type { FieldHook, FormHook } from '../../../../shared_imports'; import { Field, Form, getUseField, UseField } from '../../../../shared_imports'; -import { defaultRiskScoreBySeverity, severityOptions } from './data'; +import { defaultRiskScoreBySeverity } from './data'; import { isUrlInvalid } from '../../../../common/utils/validators'; import { schema as defaultSchema } from './schema'; import * as I18n from './translations'; @@ -32,7 +32,7 @@ import { StepContentWrapper } from '../../../rule_creation/components/step_conte import { MarkdownEditorForm } from '../../../../common/components/markdown_editor/eui_form'; import { SeverityField } from '../severity_mapping'; import { RiskScoreField } from '../risk_score_mapping'; -import { AutocompleteField } from '../autocomplete_field'; +import { EsFieldSelectorField } from '../es_field_selector_field'; import { useFetchIndex } from '../../../../common/containers/source'; import { DEFAULT_INDICATOR_SOURCE_PATH, @@ -176,7 +176,6 @@ const StepAboutRuleComponent: FC = ({ dataTestSubj: 'detectionEngineStepAboutRuleSeverityField', idAria: 'detectionEngineStepAboutRuleSeverityField', isDisabled: isLoading || indexPatternLoading, - options: severityOptions, indices: indexPattern, setRiskScore, }} @@ -376,14 +375,13 @@ const StepAboutRuleComponent: FC = ({ ) : ( )} @@ -391,14 +389,13 @@ const StepAboutRuleComponent: FC = ({ {!!timestampOverride && timestampOverride !== '@timestamp' && ( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx index 83d79debb757..66c599d0dc72 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/schema.tsx @@ -7,12 +7,22 @@ import { i18n } from '@kbn/i18n'; -import type { FormSchema, ValidationFunc, ERROR_CODE } from '../../../../shared_imports'; +import type { + FormSchema, + ValidationFunc, + ERROR_CODE, + ValidationError, +} from '../../../../shared_imports'; import { FIELD_TYPES, fieldValidators, VALIDATION_TYPES } from '../../../../shared_imports'; -import type { AboutStepRule } from '../../../../detections/pages/detection_engine/rules/types'; +import type { + AboutStepRiskScore, + AboutStepRule, +} from '../../../../detections/pages/detection_engine/rules/types'; import { OptionalFieldLabel } from '../optional_field_label'; import { isUrlInvalid } from '../../../../common/utils/validators'; import * as I18n from './translations'; +import { defaultRiskScoreValidator } from '../../validators/default_risk_score_validator'; +import { maxSignalsValidatorFactory } from '../../validators/max_signals_validator_factory'; const { emptyField } = fieldValidators; @@ -109,6 +119,11 @@ export const schema: FormSchema = { } ), labelAppend: OptionalFieldLabel, + validations: [ + { + validator: maxSignalsValidatorFactory(), + }, + ], }, isAssociatedToEndpointList: { type: FIELD_TYPES.CHECKBOX, @@ -129,6 +144,18 @@ export const schema: FormSchema = { value: {}, mapping: {}, isMappingChecked: {}, + validations: [ + { + validator: ( + ...args: Parameters> + ): ValidationError | undefined => { + const [{ value: fieldValue, path }] = args; + const defaultRiskScore = fieldValue.value; + + return defaultRiskScoreValidator(defaultRiskScore, path); + }, + }, + ], }, references: { label: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx index 9bcf35fdb13c..f1dcfc74e792 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx @@ -365,7 +365,7 @@ describe.skip('StepDefineRule', () => { ); }); - it('submits saved early required fields without the "ecs" property', async () => { + it('submits saved earlier required fields', async () => { const initialState = { index: ['test-index'], queryBar: { @@ -390,7 +390,7 @@ describe.skip('StepDefineRule', () => { expect(handleSubmit).toHaveBeenCalledWith( expect.objectContaining({ - requiredFields: [{ name: 'host.name', type: 'string' }], + requiredFields: initialState.requiredFields, }), true ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 46dde209804f..7d80ce742325 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -19,6 +19,7 @@ import type { List, } from '@kbn/securitysolution-io-ts-list-types'; import type { + RiskScoreMappingItem, Threats, ThreatSubtechnique, ThreatTechnique, @@ -57,6 +58,8 @@ import type { RuleCreateProps, AlertSuppression, RequiredFieldInput, + SeverityMapping, + RelatedIntegrationArray, } from '../../../../../common/api/detection_engine/model/rule_schema'; import { stepActionsDefaultValue } from '../../../rule_creation/components/step_rule_actions'; import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/constants'; @@ -407,8 +410,9 @@ export const getStepDataDataSource = ( /** * Strips away form rows that were not filled out by the user */ -const removeEmptyRequiredFields = (requiredFields: RequiredFieldInput[]): RequiredFieldInput[] => - requiredFields.filter((field) => field.name !== '' && field.type !== ''); +export const removeEmptyRequiredFields = ( + requiredFields: RequiredFieldInput[] +): RequiredFieldInput[] => requiredFields.filter((field) => field.name !== '' && field.type !== ''); export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { const stepData = getStepDataDataSource(defineStepData); @@ -418,7 +422,9 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep const baseFields = { type: ruleType, - related_integrations: defineStepData.relatedIntegrations?.filter((ri) => !isEmpty(ri.package)), + related_integrations: defineStepData.relatedIntegrations + ? filterOutEmptyRelatedIntegrations(defineStepData.relatedIntegrations) + : undefined, ...(timeline.id != null && timeline.title != null && { timeline_id: timeline.id, @@ -624,12 +630,12 @@ export const formatAboutStepData = ( : { field_names: investigationFields }, risk_score: riskScore.value, risk_score_mapping: riskScore.isMappingChecked - ? riskScore.mapping.filter((m) => m.field != null && m.field !== '') + ? filterOutEmptyRiskScoreMappingItems(riskScore.mapping) : [], rule_name_override: ruleNameOverride !== '' ? ruleNameOverride : undefined, severity: severity.value, severity_mapping: severity.isMappingChecked - ? severity.mapping.filter((m) => m.field != null && m.field !== '' && m.value != null) + ? filterOutEmptySeverityMappingItems(severity.mapping) : [], threat: filterEmptyThreats(threat).map((singleThreat) => ({ ...singleThreat, @@ -645,6 +651,15 @@ export const formatAboutStepData = ( return resp; }; +export const filterOutEmptyRiskScoreMappingItems = (riskScoreMapping: RiskScoreMappingItem[]) => + riskScoreMapping.filter((m) => m.field != null && m.field !== ''); + +export const filterOutEmptySeverityMappingItems = (severityMapping: SeverityMapping) => + severityMapping.filter((m) => m.field != null && m.field !== '' && m.value != null); + +export const filterOutEmptyRelatedIntegrations = (relatedIntegrations: RelatedIntegrationArray) => + relatedIntegrations.filter((ri) => !isEmpty(ri.package)); + export const isRuleAction = ( action: AlertingRuleAction | AlertingRuleSystemAction, actionTypeRegistry: ActionTypeRegistryContract diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/default_risk_score_validator.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/default_risk_score_validator.ts new file mode 100644 index 000000000000..6fd7255d4829 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/default_risk_score_validator.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const MIN_RISK_SCORE = 0; +export const MAX_RISK_SCORE = 100; + +export function defaultRiskScoreValidator(defaultRiskScore: unknown, path: string) { + return isDefaultRiskScoreWithinRange(defaultRiskScore) + ? undefined + : { + path, + message: i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleCreation.validation.defaultRiskScoreOutOfRangeValidationError', + { + values: { min: MIN_RISK_SCORE, max: MAX_RISK_SCORE }, + defaultMessage: 'Risk score must be between {min} and {max}.', + } + ), + }; +} + +function isDefaultRiskScoreWithinRange(value: unknown) { + return typeof value === 'number' && value >= MIN_RISK_SCORE && value <= MAX_RISK_SCORE; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/max_signals_validator_factory.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/max_signals_validator_factory.ts new file mode 100644 index 000000000000..b50e0dc4662a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/max_signals_validator_factory.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { ERROR_CODE } from '../../../shared_imports'; +import { fieldValidators, type FormData, type ValidationFunc } from '../../../shared_imports'; + +export const MIN_VALUE = 1; + +export function maxSignalsValidatorFactory(): ValidationFunc { + return fieldValidators.numberGreaterThanField({ + than: MIN_VALUE, + allowEquality: true, + message: i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleCreation.validation.maxSignals.greaterThanError', + { + values: { min: MIN_VALUE }, + defaultMessage: 'Max alerts must be greater than {min}.', + } + ), + }); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/common_rule_field_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/common_rule_field_edit.tsx index fefd35fcfaf6..3f10ce014d82 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/common_rule_field_edit.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/common_rule_field_edit.tsx @@ -7,16 +7,247 @@ import React from 'react'; import { RuleFieldEditFormWrapper } from './fields/rule_field_edit_form_wrapper'; -import { NameEdit, nameSchema } from './fields/name'; import type { UpgradeableCommonFields } from '../../../../model/prebuilt_rule_upgrade/fields'; +import { + BuildingBlockEdit, + buildingBlockSchema, + buildingBlockDeserializer, + buildingBlockSerializer, +} from './fields/building_block'; +import { DescriptionEdit, descriptionSchema } from './fields/description'; +import { + FalsePositivesEdit, + falsePositivesSchema, + falsePositivesSerializer, + falsePositivesDeserializer, +} from './fields/false_positives'; +import { + InvestigationFieldsEdit, + investigationFieldsSchema, + investigationFieldsDeserializer, + investigationFieldsSerializer, +} from './fields/investigation_fields'; +import { + MaxSignalsEdit, + maxSignalsDeserializer, + maxSignalsSchema, + maxSignalsSerializer, +} from './fields/max_signals'; +import { NameEdit, nameSchema } from './fields/name'; +import { NoteEdit, noteSchema } from './fields/note'; +import { ReferencesEdit, referencesSchema, referencesSerializer } from './fields/references'; +import { + RelatedIntegrationsEdit, + relatedIntegrationsSchema, + relatedIntegrationsDeserializer, + relatedIntegrationsSerializer, +} from './fields/related_integrations'; +import { + RequiredFieldsEdit, + requiredFieldsSchema, + requiredFieldsDeserializer, + requiredFieldsSerializer, +} from './fields/required_fields'; +import { + RiskScoreEdit, + riskScoreDeserializer, + riskScoreSchema, + riskScoreSerializer, +} from './fields/risk_score'; +import { + RiskScoreMappingEdit, + riskScoreMappingDeserializer, + riskScoreMappingSerializer, +} from './fields/risk_score_mapping'; +import { + RuleNameOverrideEdit, + ruleNameOverrideDeserializer, + ruleNameOverrideSerializer, + ruleNameOverrideSchema, +} from './fields/rule_name_override'; +import { + RuleScheduleEdit, + ruleScheduleSchema, + ruleScheduleDeserializer, + ruleScheduleSerializer, +} from './fields/rule_schedule'; +import { SetupEdit, setupSchema } from './fields/setup'; +import { SeverityEdit } from './fields/severity'; +import { + SeverityMappingEdit, + severityMappingDeserializer, + severityMappingSerializer, +} from './fields/severity_mapping'; +import { TagsEdit, tagsSchema } from './fields/tags'; +import { ThreatEdit, threatSchema, threatSerializer } from './fields/threat'; +import { + TimelineTemplateEdit, + timelineTemplateDeserializer, + timelineTemplateSchema, + timelineTemplateSerializer, +} from './fields/timeline_template'; +import { + TimestampOverrideEdit, + timestampOverrideDeserializer, + timestampOverrideSerializer, + timestampOverrideSchema, +} from './fields/timestamp_override'; + interface CommonRuleFieldEditProps { fieldName: UpgradeableCommonFields; } +/* eslint-disable-next-line complexity */ export function CommonRuleFieldEdit({ fieldName }: CommonRuleFieldEditProps) { switch (fieldName) { + case 'building_block': + return ( + + ); + case 'description': + return ( + + ); + case 'false_positives': + return ( + + ); + case 'investigation_fields': + return ( + + ); + case 'max_signals': + return ( + + ); case 'name': return ; + case 'note': + return ; + case 'references': + return ( + + ); + case 'related_integrations': + return ( + + ); + case 'required_fields': + return ( + + ); + case 'risk_score': + return ( + + ); + case 'risk_score_mapping': + return ( + + ); + case 'rule_name_override': + return ( + + ); + case 'rule_schedule': + return ( + + ); + case 'setup': + return ; + case 'severity': + return ; + case 'severity_mapping': + return ( + + ); + case 'tags': + return ; + case 'timeline_template': + return ( + + ); + case 'timestamp_override': + return ( + + ); + case 'threat': + return ( + + ); default: return null; // Will be replaced with `assertUnreachable(fieldName)` once all fields are implemented } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/building_block.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/building_block.tsx new file mode 100644 index 000000000000..3b21ff76f07a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/building_block.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { Field, UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { BuildingBlockObject } from '../../../../../../../../common/api/detection_engine'; + +export const buildingBlockSchema = { isBuildingBlock: schema.isBuildingBlock } as FormSchema<{ + isBuildingBlock: boolean; +}>; + +export function BuildingBlockEdit(): JSX.Element { + return ; +} + +export function buildingBlockDeserializer(defaultValue: FormData) { + return { + isBuildingBlock: defaultValue.building_block, + }; +} + +export function buildingBlockSerializer(formData: FormData): { + building_block: BuildingBlockObject | undefined; +} { + return { building_block: formData.isBuildingBlock ? { type: 'default' } : undefined }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/description.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/description.tsx new file mode 100644 index 000000000000..c2d279c0d72e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/description.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema } from '../../../../../../../shared_imports'; +import { Field, UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { RuleDescription } from '../../../../../../../../common/api/detection_engine'; + +export const descriptionSchema = { description: schema.description } as FormSchema<{ + description: RuleDescription; +}>; + +const componentProps = { + euiFieldProps: { + fullWidth: true, + compressed: true, + }, +}; + +export function DescriptionEdit(): JSX.Element { + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/false_positives.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/false_positives.tsx new file mode 100644 index 000000000000..ddb23732b187 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/false_positives.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { isEmpty } from 'lodash'; +import * as i18n from '../../../../../../rule_creation_ui/components/step_about_rule/translations'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { RuleFalsePositiveArray } from '../../../../../../../../common/api/detection_engine'; +import { AddItem } from '../../../../../../rule_creation_ui/components/add_item_form'; + +export const falsePositivesSchema = { falsePositives: schema.falsePositives } as FormSchema<{ + falsePositives: RuleFalsePositiveArray; +}>; + +const componentProps = { + addText: i18n.ADD_FALSE_POSITIVE, +}; + +export function FalsePositivesEdit(): JSX.Element { + return ; +} + +export function falsePositivesDeserializer(defaultValue: FormData) { + return { + falsePositives: defaultValue.false_positives, + }; +} + +export function falsePositivesSerializer(formData: FormData): { + false_positives: RuleFalsePositiveArray; +} { + const falsePositives: RuleFalsePositiveArray = formData.falsePositives; + + return { + /* Remove empty items from the falsePositives array */ + false_positives: falsePositives.filter((item) => !isEmpty(item)), + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/investigation_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/investigation_fields.tsx new file mode 100644 index 000000000000..971174f45539 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/investigation_fields.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { + DiffableRule, + InvestigationFields, + RuleFalsePositiveArray, +} from '../../../../../../../../common/api/detection_engine'; +import { MultiSelectFieldsAutocomplete } from '../../../../../../rule_creation_ui/components/multi_select_fields'; +import { useAllEsqlRuleFields } from '../../../../../../rule_creation_ui/hooks'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import { getUseRuleIndexPatternParameters } from '../utils'; + +export const investigationFieldsSchema = { + investigationFields: schema.investigationFields, +} as FormSchema<{ + investigationFields: RuleFalsePositiveArray; +}>; + +interface InvestigationFieldsEditProps { + finalDiffableRule: DiffableRule; +} + +export function InvestigationFieldsEdit({ + finalDiffableRule, +}: InvestigationFieldsEditProps): JSX.Element { + const { type } = finalDiffableRule; + + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + const { fields: investigationFields, isLoading: isInvestigationFieldsLoading } = + useAllEsqlRuleFields({ + esqlQuery: type === 'esql' ? finalDiffableRule.esql_query.query : undefined, + indexPatternsFields: indexPattern.fields, + }); + + return ( + + ); +} + +export function investigationFieldsDeserializer(defaultValue: FormData) { + return { + investigationFields: defaultValue.investigation_fields?.field_names ?? [], + }; +} + +export function investigationFieldsSerializer(formData: FormData): { + investigation_fields: InvestigationFields | undefined; +} { + const hasInvestigationFields = formData.investigationFields.length > 0; + + return { + investigation_fields: hasInvestigationFields + ? { + field_names: formData.investigationFields, + } + : undefined, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/max_signals.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/max_signals.tsx new file mode 100644 index 000000000000..dd7774eaa5ac --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/max_signals.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { MaxSignals as MaxSignalsType } from '../../../../../../../../common/api/detection_engine'; +import { DEFAULT_MAX_SIGNALS } from '../../../../../../../../common/constants'; +import { MaxSignals } from '../../../../../../rule_creation_ui/components/max_signals'; + +export const maxSignalsSchema = { maxSignals: schema.maxSignals } as FormSchema<{ + maxSignals: boolean; +}>; + +const componentProps = { + placeholder: DEFAULT_MAX_SIGNALS, +}; + +export function MaxSignalsEdit(): JSX.Element { + return ; +} + +export function maxSignalsDeserializer(defaultValue: FormData) { + return { + maxSignals: defaultValue.max_signals, + }; +} + +export function maxSignalsSerializer(formData: FormData): { + max_signals: MaxSignalsType; +} { + return { + max_signals: Number.isSafeInteger(formData.maxSignals) + ? formData.maxSignals + : DEFAULT_MAX_SIGNALS, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/name.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/name.tsx index 10ae6cffbe50..d602da90f34b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/name.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/name.tsx @@ -13,16 +13,12 @@ import type { RuleName } from '../../../../../../../../common/api/detection_engi export const nameSchema = { name: schema.name } as FormSchema<{ name: RuleName }>; +const componentProps = { + euiFieldProps: { + fullWidth: true, + }, +}; + export function NameEdit(): JSX.Element { - return ( - - ); + return ; } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/note.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/note.tsx new file mode 100644 index 000000000000..3a560fd28f27 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/note.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { InvestigationGuide } from '../../../../../../../../common/api/detection_engine'; +import * as i18n from '../../../../../../rule_creation_ui/components/step_about_rule/translations'; +import { MarkdownEditorForm } from '../../../../../../../common/components/markdown_editor'; + +export const noteSchema = { note: schema.note } as FormSchema<{ + note: InvestigationGuide; +}>; + +const componentProps = { + placeholder: i18n.ADD_RULE_NOTE_HELP_TEXT, +}; + +export function NoteEdit(): JSX.Element { + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/references.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/references.tsx new file mode 100644 index 000000000000..afa4fba09d89 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/references.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { compact } from 'lodash'; +import * as i18n from '../../../../../../rule_creation_ui/components/step_about_rule/translations'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { RuleReferenceArray } from '../../../../../../../../common/api/detection_engine'; +import { AddItem } from '../../../../../../rule_creation_ui/components/add_item_form'; +import { isUrlInvalid } from '../../../../../../../common/utils/validators'; + +export const referencesSchema = { references: schema.references } as FormSchema<{ + references: RuleReferenceArray; +}>; + +const componentProps = { + addText: i18n.ADD_REFERENCE, + validate: isUrlInvalid, +}; + +export function ReferencesEdit(): JSX.Element { + return ; +} + +export function referencesSerializer(formData: FormData): { + references: RuleReferenceArray; +} { + return { + /* Remove empty items from the references array */ + references: compact(formData.references), + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/related_integrations.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/related_integrations.tsx new file mode 100644 index 000000000000..443f0b4bb775 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/related_integrations.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_define_rule/schema'; +import { RelatedIntegrations } from '../../../../../../rule_creation/components/related_integrations'; +import type { RelatedIntegrationArray } from '../../../../../../../../common/api/detection_engine'; +import { filterOutEmptyRelatedIntegrations } from '../../../../../../rule_creation_ui/pages/rule_creation/helpers'; + +export const relatedIntegrationsSchema = { + relatedIntegrations: schema.relatedIntegrations, +} as FormSchema<{ + relatedIntegrations: RelatedIntegrationArray; +}>; + +export function RelatedIntegrationsEdit(): JSX.Element { + return ; +} + +export function relatedIntegrationsDeserializer(defaultValue: FormData) { + return { + relatedIntegrations: defaultValue.related_integrations, + }; +} + +export function relatedIntegrationsSerializer(formData: FormData): { + related_integrations: RelatedIntegrationArray; +} { + const relatedIntegrations = (formData.relatedIntegrations ?? []) as RelatedIntegrationArray; + + return { + related_integrations: filterOutEmptyRelatedIntegrations(relatedIntegrations), + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/required_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/required_fields.tsx new file mode 100644 index 000000000000..3e6809e35a4f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/required_fields.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_define_rule/schema'; +import type { + DiffableRule, + RequiredFieldInput, +} from '../../../../../../../../common/api/detection_engine'; +import { RequiredFields } from '../../../../../../rule_creation/components/required_fields'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; +import { getUseRuleIndexPatternParameters } from '../utils'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import { removeEmptyRequiredFields } from '../../../../../../rule_creation_ui/pages/rule_creation/helpers'; + +export const requiredFieldsSchema = { + requiredFields: schema.requiredFields, +} as FormSchema<{ + requiredFields: RequiredFieldInput[]; +}>; + +interface RequiredFieldsEditProps { + finalDiffableRule: DiffableRule; +} + +export function RequiredFieldsEdit({ finalDiffableRule }: RequiredFieldsEditProps): JSX.Element { + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + return ( + + ); +} + +export function requiredFieldsDeserializer(defaultValue: FormData) { + return { + requiredFields: defaultValue.required_fields, + }; +} + +export function requiredFieldsSerializer(formData: FormData): { + required_fields: RequiredFieldInput[]; +} { + const requiredFields = (formData.requiredFields ?? []) as RequiredFieldInput[]; + return { + required_fields: removeEmptyRequiredFields(requiredFields), + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score.tsx new file mode 100644 index 000000000000..757521370605 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + type FormData, + type FieldHook, + UseField, + getFieldValidityAndErrorMessage, +} from '../../../../../../../shared_imports'; +import type { RiskScore } from '../../../../../../../../common/api/detection_engine'; +import { DefaultRiskScore } from '../../../../../../rule_creation_ui/components/risk_score_mapping/default_risk_score'; +import { defaultRiskScoreValidator } from '../../../../../../rule_creation_ui/validators/default_risk_score_validator'; + +export const riskScoreSchema = { + riskScore: { + validations: [ + { + validator: ({ path, value }: { path: string; value: unknown }) => + defaultRiskScoreValidator(value, path), + }, + ], + }, +}; + +export function RiskScoreEdit(): JSX.Element { + return ; +} + +interface RiskScoreEditFieldProps { + field: FieldHook; +} + +function RiskScoreEditField({ field }: RiskScoreEditFieldProps) { + const { value, setValue } = field; + const errorMessage = getFieldValidityAndErrorMessage(field).errorMessage ?? undefined; + + return ; +} + +export function riskScoreDeserializer(defaultValue: FormData) { + return { + riskScore: defaultValue.risk_score, + }; +} + +export function riskScoreSerializer(formData: FormData): { + risk_score: RiskScore; +} { + return { + risk_score: formData.riskScore, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score_mapping.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score_mapping.tsx new file mode 100644 index 000000000000..a1e5e3f80630 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/risk_score_mapping.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import { type FormData, type FieldHook, UseField } from '../../../../../../../shared_imports'; +import type { + DiffableRule, + RiskScoreMapping, +} from '../../../../../../../../common/api/detection_engine'; +import { RiskScoreOverride } from '../../../../../../rule_creation_ui/components/risk_score_mapping/risk_score_override'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; +import { getUseRuleIndexPatternParameters } from '../utils'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import { filterOutEmptyRiskScoreMappingItems } from '../../../../../../rule_creation_ui/pages/rule_creation/helpers'; + +interface RiskScoreMappingEditProps { + finalDiffableRule: DiffableRule; +} + +export function RiskScoreMappingEdit({ finalDiffableRule }: RiskScoreMappingEditProps) { + return ( + + ); +} + +interface RiskScoreMappingFieldProps { + field: FieldHook<{ isMappingChecked: boolean; mapping: RiskScoreMapping }>; + finalDiffableRule: DiffableRule; +} + +function RiskScoreMappingField({ field, finalDiffableRule }: RiskScoreMappingFieldProps) { + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + const { value, setValue } = field; + + const handleToggleMappingChecked = useCallback(() => { + setValue((prevValue) => ({ + ...prevValue, + isMappingChecked: !prevValue.isMappingChecked, + })); + }, [setValue]); + + const handleMappingChange = useCallback( + ([newField]: DataViewFieldBase[]): void => { + const mapping = [ + { + field: newField?.name ?? '', + operator: 'equals' as const, + value: '', + }, + ]; + + setValue((prevValue) => ({ + ...prevValue, + mapping, + })); + }, + [setValue] + ); + + return ( + + ); +} + +export function riskScoreMappingDeserializer(defaultValue: FormData) { + return { + riskScoreMapping: { + isMappingChecked: defaultValue.risk_score_mapping.length > 0, + mapping: defaultValue.risk_score_mapping, + }, + }; +} + +export function riskScoreMappingSerializer(formData: FormData): { + risk_score_mapping: RiskScoreMapping; +} { + return { + risk_score_mapping: formData.riskScoreMapping.isMappingChecked + ? filterOutEmptyRiskScoreMappingItems(formData.riskScoreMapping.mapping) + : [], + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_field_edit_form_wrapper.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_field_edit_form_wrapper.tsx index 26a2574489b1..1b45bea28880 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_field_edit_form_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_field_edit_form_wrapper.tsx @@ -27,13 +27,13 @@ export type FieldDeserializerFn = ( interface RuleFieldEditFormWrapperProps { component: RuleFieldEditComponent; - ruleFieldFormSchema: FormSchema; + ruleFieldFormSchema?: FormSchema; deserializer?: FieldDeserializerFn; serializer?: (formData: FormData) => FormData; } /** - * FieldFormWrapper component manages form state and renders "Save" and "Cancel" buttons. + * RuleFieldEditFormWrapper component manages form state and renders "Save" and "Cancel" buttons. * * @param {Object} props - Component props. * @param {React.ComponentType} props.component - Field component to be wrapped. diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_name_override.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_name_override.tsx new file mode 100644 index 000000000000..d13395643650 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_name_override.tsx @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { + DiffableRule, + RuleNameOverrideObject, +} from '../../../../../../../../common/api/detection_engine'; +import { EsFieldSelectorField } from '../../../../../../rule_creation_ui/components/es_field_selector_field'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import { getUseRuleIndexPatternParameters } from '../utils'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; + +export const ruleNameOverrideSchema = { ruleNameOverride: schema.ruleNameOverride } as FormSchema<{ + ruleNameOverride: string; +}>; + +interface RuleNameOverrideEditProps { + finalDiffableRule: DiffableRule; +} + +export function RuleNameOverrideEdit({ + finalDiffableRule, +}: RuleNameOverrideEditProps): JSX.Element { + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + const componentProps = useMemo( + () => ({ + fieldType: 'string', + indices: indexPattern, + isDisabled: isIndexPatternLoading, + }), + [indexPattern, isIndexPatternLoading] + ); + + return ( + + ); +} + +export function ruleNameOverrideDeserializer(defaultValue: FormData) { + return { + ruleNameOverride: defaultValue.rule_name_override?.field_name ?? '', + }; +} + +export function ruleNameOverrideSerializer(formData: FormData): { + rule_name_override: RuleNameOverrideObject | undefined; +} { + return { + rule_name_override: formData.ruleNameOverride + ? { field_name: formData.ruleNameOverride } + : undefined, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx new file mode 100644 index 000000000000..12e7bbea9a20 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { parseDuration } from '@kbn/alerting-plugin/common'; +import { type FormSchema, type FormData, UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_schedule_rule/schema'; +import type { RuleSchedule } from '../../../../../../../../common/api/detection_engine'; +import { ScheduleItem } from '../../../../../../rule_creation/components/schedule_item_form'; +import { secondsToDurationString } from '../../../../../../../detections/pages/detection_engine/rules/helpers'; + +export const ruleScheduleSchema = { + interval: schema.interval, + from: schema.from, +} as FormSchema<{ + interval: string; + from: string; +}>; + +const componentProps = { + minimumValue: 1, +}; + +export function RuleScheduleEdit(): JSX.Element { + return ( + <> + + + + ); +} + +export function ruleScheduleDeserializer(defaultValue: FormData) { + const lookbackSeconds = parseDuration(defaultValue.rule_schedule.lookback) / 1000; + const lookbackHumanized = secondsToDurationString(lookbackSeconds); + + return { + interval: defaultValue.rule_schedule.interval, + from: lookbackHumanized, + }; +} + +export function ruleScheduleSerializer(formData: FormData): { + rule_schedule: RuleSchedule; +} { + return { + rule_schedule: { + interval: formData.interval, + lookback: formData.from, + }, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/setup.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/setup.tsx new file mode 100644 index 000000000000..8304641d1b16 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/setup.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { SetupGuide } from '../../../../../../../../common/api/detection_engine'; +import * as i18n from '../../../../../../rule_creation_ui/components/step_about_rule/translations'; +import { MarkdownEditorForm } from '../../../../../../../common/components/markdown_editor'; + +export const setupSchema = { setup: schema.setup } as FormSchema<{ + setup: SetupGuide; +}>; + +const componentProps = { + placeholder: i18n.ADD_RULE_SETUP_HELP_TEXT, + includePlugins: false, +}; + +export function SetupEdit(): JSX.Element { + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity.tsx new file mode 100644 index 000000000000..1a6bf03ea67f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { type FieldHook, UseField } from '../../../../../../../shared_imports'; +import type { Severity } from '../../../../../../../../common/api/detection_engine'; +import { DefaultSeverity } from '../../../../../../rule_creation_ui/components/severity_mapping/default_severity'; + +export function SeverityEdit(): JSX.Element { + return ; +} + +interface SeverityEditFieldProps { + field: FieldHook; +} + +function SeverityEditField({ field }: SeverityEditFieldProps) { + const { value, setValue } = field; + + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity_mapping.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity_mapping.tsx new file mode 100644 index 000000000000..d4206549bfa1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/severity_mapping.tsx @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import { type FieldHook, type FormData, UseField } from '../../../../../../../shared_imports'; +import type { + DiffableRule, + Severity, + SeverityMapping, +} from '../../../../../../../../common/api/detection_engine'; +import { SeverityOverride } from '../../../../../../rule_creation_ui/components/severity_mapping/severity_override'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; +import { getUseRuleIndexPatternParameters } from '../utils'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import { fillEmptySeverityMappings } from '../../../../../../../detections/pages/detection_engine/rules/helpers'; +import { filterOutEmptySeverityMappingItems } from '../../../../../../rule_creation_ui/pages/rule_creation/helpers'; + +interface SeverityMappingEditProps { + finalDiffableRule: DiffableRule; +} + +export function SeverityMappingEdit({ finalDiffableRule }: SeverityMappingEditProps): JSX.Element { + return ( + + ); +} + +interface SeverityMappingFieldProps { + field: FieldHook<{ + isMappingChecked: boolean; + mapping: SeverityMapping; + }>; + finalDiffableRule: DiffableRule; +} + +function SeverityMappingField({ field, finalDiffableRule }: SeverityMappingFieldProps) { + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + const { value, setValue } = field; + + const handleFieldChange = useCallback( + (index: number, severity: Severity, [newField]: DataViewFieldBase[]): void => { + setValue((prevValue) => { + const newMappingItem: SeverityMapping = [ + { + ...prevValue.mapping[index], + field: newField?.name ?? '', + value: newField != null ? prevValue.mapping[index].value : '', + operator: 'equals', + severity, + }, + ]; + + return { + ...prevValue, + mapping: [ + ...prevValue.mapping.slice(0, index), + ...newMappingItem, + ...prevValue.mapping.slice(index + 1), + ], + }; + }); + }, + [setValue] + ); + + const handleFieldMatchValueChange = useCallback( + (index: number, severity: Severity, newMatchValue: string): void => { + setValue((prevValue) => { + const newMappingItem: SeverityMapping = [ + { + ...prevValue.mapping[index], + field: prevValue.mapping[index].field, + value: + prevValue.mapping[index].field != null && prevValue.mapping[index].field !== '' + ? newMatchValue + : '', + operator: 'equals', + severity, + }, + ]; + + return { + ...prevValue, + mapping: [ + ...prevValue.mapping.slice(0, index), + ...newMappingItem, + ...prevValue.mapping.slice(index + 1), + ], + }; + }); + }, + [setValue] + ); + + const handleSeverityMappingChecked = useCallback(() => { + setValue((prevValue) => ({ + ...prevValue, + isMappingChecked: !prevValue.isMappingChecked, + })); + }, [setValue]); + + return ( + + ); +} + +export function severityMappingDeserializer(defaultValue: FormData) { + return { + severityMapping: { + isMappingChecked: defaultValue.severity_mapping.length > 0, + mapping: fillEmptySeverityMappings(defaultValue.severity_mapping as SeverityMapping), + }, + }; +} + +export function severityMappingSerializer(formData: FormData): { + severity_mapping: SeverityMapping; +} { + return { + severity_mapping: formData.severityMapping.isMappingChecked + ? filterOutEmptySeverityMappingItems(formData.severityMapping.mapping) + : [], + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/tags.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/tags.tsx new file mode 100644 index 000000000000..063b7f4f48b8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/tags.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema } from '../../../../../../../shared_imports'; +import { Field, UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { RuleTagArray } from '../../../../../../../../common/api/detection_engine'; + +export const tagsSchema = { tags: schema.tags } as FormSchema<{ name: RuleTagArray }>; + +const componentProps = { + euiFieldProps: { + fullWidth: true, + placeholder: '', + }, +}; + +export function TagsEdit(): JSX.Element { + return ; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threat.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threat.tsx new file mode 100644 index 000000000000..fbc0541ff50f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threat.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { type FormSchema, type FormData, UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import type { ThreatArray } from '../../../../../../../../common/api/detection_engine'; +import { AddMitreAttackThreat } from '../../../../../../rule_creation_ui/components/mitre'; +import { filterEmptyThreats } from '../../../../../../rule_creation_ui/pages/rule_creation/helpers'; + +export const threatSchema = { threat: schema.threat } as FormSchema<{ threat: ThreatArray }>; + +export function ThreatEdit(): JSX.Element { + return ; +} + +export function threatSerializer(formData: FormData): { + threat: ThreatArray; +} { + return { + threat: filterEmptyThreats(formData.threat).map((singleThreat) => ({ + ...singleThreat, + framework: 'MITRE ATT&CK', + })), + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timeline_template.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timeline_template.tsx new file mode 100644 index 000000000000..1f7ef2eaf05b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timeline_template.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { UseField } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_define_rule/schema'; +import type { TimelineTemplateReference } from '../../../../../../../../common/api/detection_engine'; +import { + PickTimeline, + type FieldValueTimeline, +} from '../../../../../../rule_creation/components/pick_timeline'; + +export const timelineTemplateSchema = { timeline: schema.timeline } as FormSchema<{ + timeline: FieldValueTimeline; +}>; + +export function TimelineTemplateEdit(): JSX.Element { + return ; +} + +export function timelineTemplateDeserializer(defaultValue: FormData) { + return { + timeline: { + id: defaultValue.timeline_template?.timeline_id ?? null, + title: defaultValue.timeline_template?.timeline_title ?? null, + }, + }; +} + +export function timelineTemplateSerializer(formData: FormData): { + timeline_template: TimelineTemplateReference | undefined; +} { + if (!formData.timeline.id) { + return { timeline_template: undefined }; + } + + return { + timeline_template: { + timeline_id: formData.timeline.id, + timeline_title: formData.timeline.title, + }, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timestamp_override.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timestamp_override.tsx new file mode 100644 index 000000000000..a57d538fab47 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/timestamp_override.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import type { FormSchema, FormData } from '../../../../../../../shared_imports'; +import { Field, UseField, useFormData } from '../../../../../../../shared_imports'; +import { schema } from '../../../../../../rule_creation_ui/components/step_about_rule/schema'; +import { EsFieldSelectorField } from '../../../../../../rule_creation_ui/components/es_field_selector_field'; +import { useDefaultIndexPattern } from '../../../../../hooks/use_default_index_pattern'; +import { getUseRuleIndexPatternParameters } from '../utils'; +import { useRuleIndexPattern } from '../../../../../../rule_creation_ui/pages/form'; +import type { + DiffableRule, + TimestampOverrideObject, +} from '../../../../../../../../common/api/detection_engine'; + +export const timestampOverrideSchema = { + timestampOverride: schema.timestampOverride, + timestampOverrideFallbackDisabled: schema.timestampOverrideFallbackDisabled, +} as FormSchema<{ + timestampOverride: string; + timestampOverrideFallbackDisabled: boolean | undefined; +}>; + +interface TimestampOverrideEditProps { + finalDiffableRule: DiffableRule; +} + +export function TimestampOverrideEdit({ + finalDiffableRule, +}: TimestampOverrideEditProps): JSX.Element { + const defaultIndexPattern = useDefaultIndexPattern(); + const indexPatternParameters = getUseRuleIndexPatternParameters( + finalDiffableRule, + defaultIndexPattern + ); + const { indexPattern, isIndexPatternLoading } = useRuleIndexPattern(indexPatternParameters); + + const componentProps = useMemo( + () => ({ + fieldType: 'date', + indices: indexPattern, + isDisabled: isIndexPatternLoading, + }), + [indexPattern, isIndexPatternLoading] + ); + + return ( + <> + + + + ); +} + +function TimestampFallbackDisabled() { + const [formData] = useFormData(); + const { timestampOverride } = formData; + + if (timestampOverride && timestampOverride !== '@timestamp') { + return ; + } + + return null; +} + +export function timestampOverrideDeserializer(defaultValue: FormData) { + return { + timestampOverride: defaultValue.timestamp_override.field_name, + timestampOverrideFallbackDisabled: defaultValue.timestamp_override.fallback_disabled ?? false, + }; +} + +export function timestampOverrideSerializer(formData: FormData): { + timestamp_override: TimestampOverrideObject | undefined; +} { + if (formData.timestampOverride === '') { + return { timestamp_override: undefined }; + } + + return { + timestamp_override: { + field_name: formData.timestampOverride, + fallback_disabled: formData.timestampOverrideFallbackDisabled, + }, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/utils.ts new file mode 100644 index 000000000000..bd78bb5e9ed2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/utils.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataSourceType } from '../../../../../../detections/pages/detection_engine/rules/types'; +import { DataSourceType as DataSourceTypeSnakeCase } from '../../../../../../../common/api/detection_engine'; +import type { DiffableRule } from '../../../../../../../common/api/detection_engine'; + +interface UseRuleIndexPatternParameters { + dataSourceType: DataSourceType; + index: string[]; + dataViewId: string | undefined; +} + +export function getUseRuleIndexPatternParameters( + finalDiffableRule: DiffableRule, + defaultIndexPattern: string[] +): UseRuleIndexPatternParameters { + if (!('data_source' in finalDiffableRule) || !finalDiffableRule.data_source) { + return { + dataSourceType: DataSourceType.IndexPatterns, + index: defaultIndexPattern, + dataViewId: undefined, + }; + } + if (finalDiffableRule.data_source.type === DataSourceTypeSnakeCase.data_view) { + return { + dataSourceType: DataSourceType.DataView, + index: [], + dataViewId: finalDiffableRule.data_source.data_view_id, + }; + } + return { + dataSourceType: DataSourceType.IndexPatterns, + index: finalDiffableRule.data_source.index_patterns, + dataViewId: undefined, + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/common_rule_field_readonly.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/common_rule_field_readonly.tsx index bc4f1928ef9b..6c502b2e4178 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/common_rule_field_readonly.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/common_rule_field_readonly.tsx @@ -45,7 +45,7 @@ export function CommonRuleFieldReadOnly({ }: CommonRuleFieldReadOnlyProps) { switch (fieldName) { case 'building_block': - return ; + return ; case 'description': return ; case 'investigation_fields': diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/building_block/building_block.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/building_block/building_block.tsx index 84edd7932a2d..7e64c140e6d7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/building_block/building_block.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_readonly/fields/building_block/building_block.tsx @@ -9,8 +9,17 @@ import React from 'react'; import { EuiDescriptionList } from '@elastic/eui'; import * as ruleDetailsI18n from '../../../../translations'; import { BuildingBlock } from '../../../../rule_about_section'; +import type { BuildingBlockObject } from '../../../../../../../../../common/api/detection_engine'; + +interface BuildingBlockReadOnlyProps { + buildingBlock?: BuildingBlockObject; +} + +export function BuildingBlockReadOnly({ buildingBlock }: BuildingBlockReadOnlyProps) { + if (!buildingBlock || !buildingBlock.type) { + return null; + } -export function BuildingBlockReadOnly() { return ( { + if (threat.length === 0) { + return null; + } + return ( Date: Tue, 12 Nov 2024 11:21:31 +0100 Subject: [PATCH 042/100] [DOCS] Search AI assistant (#199602) --- docs/search/index.asciidoc | 2 +- .../images/ai-assistant-button.png | Bin 0 -> 8159 bytes .../images/ai-assistant-welcome-chat.png | Bin 0 -> 145051 bytes .../search/search-ai-assistant/index.asciidoc | 144 +++++++++++++++++- 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 docs/search/search-ai-assistant/images/ai-assistant-button.png create mode 100644 docs/search/search-ai-assistant/images/ai-assistant-welcome-chat.png diff --git a/docs/search/index.asciidoc b/docs/search/index.asciidoc index 4d2ee436327b..517503772d3c 100644 --- a/docs/search/index.asciidoc +++ b/docs/search/index.asciidoc @@ -10,7 +10,7 @@ The *Search* space in the {kib} UI contains the following GUI features: * https://www.elastic.co/guide/en/elasticsearch/reference/current/search-application-overview.html[Search Applications] * https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-overview.html[Behavioral Analytics] * <> -* <> +* <> * Dev Tools <> [float] diff --git a/docs/search/search-ai-assistant/images/ai-assistant-button.png b/docs/search/search-ai-assistant/images/ai-assistant-button.png new file mode 100644 index 0000000000000000000000000000000000000000..640d81e32d46b6fc581e33c0bad1f924c7fc3436 GIT binary patch literal 8159 zcmb_=byQr-^XK4$yF(Z}*vudaF2UX12Y1(m5M(C73BlchTL{5|C4&b-aCZyAf)j%M zChxs{-|yKy``2!tQ@83?)u-xqb)9>xPD!ZFqxq{=gU^IFCU4f8F08IzO??Jay%_NRuv zEEy<;2IEO@0+_ocpgxSyf>P(*IPXInN1grW^#ujhu~=;<99Vu!MxDYd!hv_!-!kuW zL<6^%#R28Ht5Nc;doxVMaa*>*#la6tJO^To(e_Hd*K zp^d8(YlLy-H|6^$oTr{yKf1FRG`FE?-xLf}Zm_P3%ty}d1sU(#Hn&t?h8QC&->?%U zB#qE+9~h2oYI7New`gTNAjPfc=wZT?$dv4P$gW1l0BKetY`Nmi5G=r`XHDE-!mCH? z2}dqQL9z1ln1Ob31V%tH4ed_}k;c^P3O6@l#)z*}$D1?=$OkF8VH@sWz6|xlb(1p~ zDw+~{h4*5y-wbzbD-}$U$_ZskY^a~&!kn*l}M=%hYRB5wjK znoZ7`emudRaDKe&@%Ed-D;u--nEOC1Dec#k)V$w_r~9@B)?fL42A?8^e=#$LhU(6a zng@m41Fjb9A9O;c3=2=6=&w?}}8% zMU;yK=LG3I=44ZSK-|dyN25Bz@dJfFVpPYTlNlYG8O8azDH$Gy(E}ns3h#)ZrAvCa zM~ZaieTe=-wg=~`d*jBgBSaf`Ro7j8<3cQ8*MWFXD!R(m6AYr*L_UjlOYxdDLk~iy z{Vd(?YrTaG@`a^d+&>GAx*dTslb2ev$vybtBA31lDDDM}e4rpTF00 z>qK!mY4t^%rvc%-YNX9VJZJeI?jJ`o<-J5E>?~(Y?bPxa1G*RXsdd%(yeZ}xRr z(N}5^%wK8Rk>VP}G-!g>gx@zX&BHm5u+Tu5@)07h2{@2D4Xa|*krmb)891skw8li! zVT9I#v)BRQ9jgE*fYN6bFKlzn^w0iY)U|mYN9bpffvbWMAU1f+D2ZebO})%M2`4-} zh!stlhAfhe)mfQ?MV@Uy-A+pPWwrcckXe$8o%A?7M}CAgcffsuvZO{dv^u!cv}x8V(z!pe__d=_49J9^qq z*cBt)bA9ZI$^ALZTImS%4p#-Pj?|aMEzEfr>6PI{cUajlo8yl4z%N!lLj-C_2yfO@{}Sr zeQrZ;qxl*?^B*zef+gSV{NH2EMKY*tCzU0qCp9N$52g<+ZGNL5jG^p}B~uzK9WJge zp48RUJJeZz$itJ(-NvKDgP-!9yH0PpEJX9F@I;AHgVKyLO|>#c-oE0CW-2*%v%#`< zg}z!P}{^E z{X%gLine1dAFe(ee3=)N@d1lR;BJ*XZXsntEsf9At2&H=m77)cHTlPBcX)I zjMK<5a|A7yUMydcCc!#kHPKk2D4-{m^ryel?(f|be>ML-zq-B9@!~zKovodkQP!BHj&N_^GK&_qZXzU&en0p3& zdG}4Ze_-a}f>Yy|cd?~yR(*5*r=Ujf*kj+1k8@SNmDCkCk6mjJs7Gk9ahPd{sY_*> zX7*)MWlv`=3m0eb4+{(%W;bS?XVYiD%Kj!)$KPf9q{-UY&ZN3g#PxW2{ENVtPoaIJ zePU`YB`Zoz3r zGzR+Rd5V|*RC)FV_l5ek>^0r>D7*Q_~ArV9=J5iIi!{%&mY=yys!I!aFe3`LW zl*3N_IAfc?F4rx$MUzr)s~-zJ~n@BSa%c*T4|Oa7Dk4G>>q=%ms2_zQV2pb`Cza^PQX8zawdW zZNltc$X!@pNH5ETn0p!Boz*>d^!mtP?UIF;1x`ws1bV~R%6$@=VlbIf&N|k!Q@#_k zQ^s#=yqgPyd{Vurs;AB39M9eQQ3{hH6$<95Ruk-VRP7Hn>@+k&B3V7`j)-)K=T?I zy{YewJwEp%^~bF_RUqFb*0=QLteK(9^}0>zp`$OxnPbU6Qd_wjgoik*B&xJNk-3qy ze<2!K&bgK16mWDLvG%a>u*lQ3G__3D*2`dZ%XSkAp5N3=$}N|leAH|EC5!8JUHdp8 zgzbfFsBJjaO_FE2v07-71n(KR`r!@q5p4IfGu;GyV!mua(@NsjM8|r}Q}Bp(k+w!q z!(k-*%8SZ(dU|eP|7@9969C0O({6cWH|X>t<~x_+~b^%8#@9CV{L z3VFm{$-e$38()&z!x9c5jXWJceZ_dK!DdD^K2?E-sSeM|pew8Mqu=?$qvYuvNFaEnJ?*{W`zBY5*^hna z+k5GC_wSiqyU+2uVm_)ye(6p9DSYzSa&EJ&BA{Vy`~BDaCScRzJl%)J&M)sPL+HKD z)|$!|$eZ$S(vNYd$`0{(NZv$SMA9X`t3_-q5q#%g596 zGafY)6+8<#7b+K$EBQ_Lec_`RH8`|5uEZt`l@jXW=BA00T6I4?2V8bs)IIjy@SI)T zFWjU;de#O;iaC^c5}cXL0&;vpJ|BNhD@qF|FCu@=DX1mokvsYHQh&U{)qTvJu}n@Y zUz|K>d(vckF3s2SdqUo&$ckUMci~#mUhnUK^o#DUwwutF?#AM~Hwz_wLx<_v;{E;) zy4HQSHVy8p9Bb?AEZQG58uF)L&Dpd+eTP%ljdFH%7k?7 z@Ag>KK}%l_Tzdjy*s6xy=E%ZtC8ko-|U!0>$9T-yAE?3b`Y==KfsVZIs2z#m(>`5v7pH{?~_yO12zks`+4Sj9m@ z1Hg@vfdEW2Isg_*LPL=h8vVbrBHBX$#$S4L0N|A~0P`OoO%(q#UZdy_=5LPi<|P0J z^+txGpaS%Ny@v}h{v)GofG2u#Dk>E}4H_~)R5>!obs3jk2D{~V?SdJbun8{cQDM()5abgV-0PPoTraH=rI@yhO$WEfj}S$A3FyzU3tZS;HWoA7AHSHFEI!t zFfb4tC%v%b!I5j=#UtKFIk$nLK>|nHFk-kUtg(KNt%6H#Q0?@kcAB?Hpw9W+LzG zj;a|dhm-(RNa8R5|HJa1jQ@o+{=Xvs%ksY?4Sel=RM^@E1#Jey6P?w z6*y?*m3~M;r5Ao2m~_n57mTe)%{<8zJ`e?rsib%^)>o#wrCdp85nb7@U-YGKC4g@p z!NmIl0@AR%dU ziFfUz=@(1-vY3-hW!doZQT@o7minM#pimGkWumk|N&6OcJ> z&s1!x6e);L-6o6Ju^ZXguLDuT?3ruWKCe_wWo9R+ZBVY4*T&)iJ$Ke~i$jIk`gqR-`^JEGl zUvwc?D?25u2acp+P+oK3g%}!eWa2DM~D zbCQk)z+r2$?`CjiXzCIHAMzCd$ah}MvwN7JxEUqD{5KW=%(#VQ|NkldkmP4-GV-0t z!Htxh*D_p25IsW&?5mT*4yjHTH{PjggKRn>4^^$6UY#n({4$5H8;cV`_Ke<4 ze?M0w?ZaHMGzG=QcjV@#s$|d9veeVFVS^pVnAHsxvA^Dhwja9&WHmVdnBCTDe%qN( zT$a`7s`kBNC!D0O^ugi>2lkpP=X#$Xh6qxe!=e^R8k2iuDW3x z7#HbuIG)^}FuGfU&qu6Zg^Xk3k0bk*GrlNxx}MeSq|n{34z>F6ULROoFZUvk$?D^E zNwG2yk~dE>H^6hHvY2k%{)6*M`0`J28L`eHY*`vY$#lO%UcTsjSkAHPwDx=li#|fM z{b$|y29-M{jDjt}(K4vw5vx)!J_1U0?Kb0S<}{c?OQjYp(pCvwAvn~U0b4z7k-^cW zd-Oa`@wOlzfnkuoj0sOtJoj4t!jA8>_xllX-RSEgmN*grcCT#;LNg;s(RWS^l^mad zJ#-kwGsBz8C@bnVDoR+6#OURWM{_hO7b=z?yE|tIt+M{Ojcf&tkv01KGF&ZoJT#sxlP;KyS@|8_#u~o zz?*fW>Sk@(Uk(T;*>iH~U>3&31~V32Drd74KwHHU^ZD5|xrcK>0^>grJOo6)bAK=c$-^3E3CY`R3yoWls~_GAc3{HLz4D}B z)yGc9cL36hnsvNL{Pc@+g{wEr>cMndoz#?6x+{mCDQeyqJVrE&bQ|jTGBnaTiA+*5 z6a9(D4byjawa_BPmaFl*&~RudO>At+x{ZA~YQ4Okb=`Jg!ET@rB>kY#SKYFW8rj%J7&$ zbIg=fQD|_G9N_Wd%ljLWaOjrv&Ar8UKNXH%HFgt9Z1O7=m;=re8ge_mE_wyd1O@?< z4f8h!FnJArgX+7x2q=eo7sKeQaoyVNYHzU0D`-(ufQI7od zw0OV|#W8;VdEq!}ZWB2?R6(oWwH|+wYOIZfx-7mPDn|V$mBb%>HDm?ACR>=(l~DiD z!~lt`8btuI+*=NuNEQZta1bP977u{yY)}cM2hp4OAVoJU(g#y)hN~gl_MN%88lCOt z6}Xoz)L`kS4ZfpdqO_lCx|01H-tz%%5z83Cz4)-i!xc5oxYptK@y#@5CM_65NaI4j zd}`!CgFrmRL_>^HW}y3mx(y4S1wMRCLDy51q!0a@bd%d#E2Fx1H?B5@Sg6)vKXWEg zEF=cA=a+5ae;=EBp~bzKHiQn_6cJ^_f@Sms(zRKtZ8!?}9(OYncsspL zMSZ{WgErWxAN7@U+D0i7TS9w+nER9>VE!6W4!H6ZKula~f}V%6038^@Ah@CB-Qlx< z*~Zv2yKD`O!1H0hDZ_)AK|6=t^QzyKU1w*$S*67aLPLYG-C2`I$x`>fFhoPVx*Eeh zmYW2Ps;OrrlB+_-5T_SiJ9k2rw`EMG)$WzJFo%}eGzEo`HYD@#a0ulUE=*wO!Y^CKsHpJ=@#r8OtZ*I+S9G6Tfgy zBIPL6%gaeInyyXyQBfgeIekLXN4IMpPQuaW|GUR~uH`P*{3wPu4yrI-)8P(}o~vNB z-388>n~*rv4)-kG%4B5pm&X4b`u6HYOLjuHCvbx#RMiO{z1Ducy>~Lxx=f)T&_L1$ z_MC76k| j{Ew!u3G7NAkyVBkfbvd0GN$iom88mYDsh!h)M?+7E@sQA{$69cy^nh zuySp6U+Gqc1%HwuJN=>EY(Q2_N2VA^F1N*LVmkwdM+d+BJxLSro3sxOFw4jwdzGqT zNPQ~c2D33N^ni(YJA)%tRN%PgG~jstu}|#;NK7yRwN=)TwJsS9o%-d_wT2KB5(y2* zFMioiXtynEmnxr;pk_nBlv5HOEQRc;mZf_6$~pr-$$$*D9S(Jgl5pTs!)geD zM~fob5jt|4PeL6~Kv@)U2U!j~DWBzKe1A&H;UL5W*OA+TZO-YwO+{5{B!u*3SDW$# zvp6wqI}_^Ca#N_=L^GudQdf8Abc1p8$ZB0~??A3QD?%DC@&XYX32Nec-CMWVIahkd z7|IPXo0Or3=c3n?FoLqaCZS*>ktXO%auY{>_#m3s9<{}L&$8SX`idD0k(==6 zPAd=QPJVETx^6)dpjVl5<>Amr+IM9?bIwqGoH>`ag4Uaw$f8Mr znV;6N_)Z2+6y})?DYs8*a9jB$Dr)3eFl9Ns?$;li`2R#dAgRLh_5Hw0josInstlJI zzpX&!zh(t@RK(SR$sS58n;rpgSw|b8md3c$s^T$Gt4a`MRmWiuPG4v~soa zUNz7%Rkd9XB!o`9I04>GZKT01rdx|Lp;A40&i>lpj#m_H3+q$W8-Mm($Rzz62{7W5 zEsvO;&a;Lw<2Zy)jrYY$6c<8*<#m9em2jLnvK(7PxVT$6D-rYkNb62tiW$umaC}Gm z%{FngsBI z@Y3g*_+l`Oo6sk(R5s$WRrS-EgqqwYY4odHL$Zk%Hi_-F0&K@5 zA8jP->Be-X7gK8$tLU!#)?iLfSeO>mcgz>hAe*+GR^BR1-u{~MxT}1m`QEY+$%b)E zi_T0UlR@H;#Bae~5_=BuDZJV7sDPmd*toAp?NPW+?Po#{-bYavB9Bz#HPc XDqF$1;FLcXTmTgXP5D|`>xlmW&1%*v literal 0 HcmV?d00001 diff --git a/docs/search/search-ai-assistant/images/ai-assistant-welcome-chat.png b/docs/search/search-ai-assistant/images/ai-assistant-welcome-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..972ad53599c8ddc81a6b797a54295e91b5568473 GIT binary patch literal 145051 zcmeFa2UJsCw>An2qNt!Eq9{dCQBXkXB?Lu9qy(i(S3smj2%#s4sMr9NDkXqGAfZI0 zNeK~9kX`~rYDAh4AoKts$=#N>e16}1#<=I6`;Rlm4`U?RWbd`sUUSVg=QG>RV*`Dy zo!o-lY;0^hwJ%?|#>U2hW@F=A+qwn#rv8}iARF5b4JS=a18q%BQ3FrtEhkq8Hnz); zqfNQY4D0uyEM7j>xXsRyet(c-_eu8j`)%i`dPe8>h(_(-eC}hol*Bvl{JT4?_^-a{ zICbKtzLoS%z_RhQ2Be#r<_TP2LVg&g4Y!>ZU#^Q}A$qZJ}?b(%8OSb1H z9ufUk`_&%tvfnlJy!dq!+oNlPuFk=ipWNUyzqw!Z!_#yn!*IzAC97~+z3bPTX1hw* zQ_`!49?7yB;CJ}>A6DdZ&RW)-Ix({CK3i7%;pLXbgr*Rsoi|->i`_HBwq9nP6JLPF zPt?R8(jSwFX}xF?wa&Tv+OlPlB`d@#9~r&!Fwm?T`|=o1_=}bUW78%rBL*i;g5N>n zgm-VtKmI|4L+usl!--=$dk#sLXzhHrdMZvfWpln*#IOi=KKqB@>i$PEoXM!eTkSP> zm~K_kGk6~!VkYqQ;`;hsvj@%l=0^5EkSIf+K?&c4oe6zv$dj|wxoS^Kkb~$xo zQ_%SjT&p$1>-P9SgRQH$nw)iK-m~`j2XA*Pe>wTVPjv4{b--iSs5_r6Hu-KkPzA1l z*^XBG!fr$_vjUKh7%eh~`OvmUHH9afHDf-oy#}9&+kd&RYRLoI&&L~LYGamkXUmMb zTm*Ibe&&7sYO!kgd4Zwjqv!HhGEdZ;6X@hM8acwKv-}ccK;)#=;8RqLYT>0(R}R&Y z=2(d_2vODJ-Ony-yK{V)!!lTWmXlqSM`G%}4iWN7!r4AG!EaDSgwmZB8I3Ud#ip2c3FrDd{=( zIV0(Nv!Y%fHIguqFzwCtvYdF*A)C=^4}Z(q6LLs<>_ui|?2D4fg!U!T9VtN#wBougPDQ)tEux^{CfNr{4rOsPcArM z8uX+2Bub124KPMJSZVnO<{DO4a?z;Q>M2Nr42(+FTVp>g%t+FRVvNhGG!o0co^__{ zIF=t1oh@Wkp5C3QZ&G69h=FxFW}9XyV#r1!Z7ELA+8=d(BAUIo1>X0T<)*o( z?Ru?L;=(g{EW}M6U$w9&9aKrav_IT7{7ZzH+H3C*(YvW|t;w~?dAJUo;)SCiJJKke z-$uXXwz;&G<02@1lUfu=7dbu1oB&PlzTEnv|3!uv@3RWKyMmtuKkRRk`FyL*UB>pd z{eJrsXDy+=4R2a6*L@?4tIuNcdpph%>H^#k&?^Y zU`NB*cF$CY5Qm89JpMlHCv5E`O1`VN&i@HKdP)A^>zy^n>u#krJ6Lpfe^h8Ju3mmA zyv7))olq+k}dX_l5|vctXw!gZXqQXdnfjR zRIZeb)qv&4yrMk3`#P(zJ7)!%DTo1XKTK@a(5>??{zNUTl`K<8}?Z~Mk_jm7nA^K9NRAL?( zW!xE+CD#7oTh_NH-!jkG-Mm$s7?$x-|H*(5U8+Ue{d)g^FU40o(B+=_dFS)+YWfA% zhv>&v+G&xuj#=q;L-rk2L_fc_=bCr=j%U343AX}$usY7#lVRIT_|SSv;HyD5XFk|P zb{_XQE~G1#ehfXWPp^JFV&t)JO=QNG8HMS(4)JXe^8v!@YHkiF?kDQ1u zl_*eb6wg-6hP>b7x~rmrr)4map)P*b(XqwW{ieGW>YBB=b)0vcT_A`(ikNc$9j9%rDlnn@TBZYNW;fys^YwYb>}_*$ z@z&!0Ucthm%7(Ytz=IwZL&ceWe8tI(usMq*9)^&EYURw{nWg6!Ph+%o@?su>6Q z+hOTwAIK!CF@Ql+=xZyvx~i7u5uFem z75d5fo3pO_lq{dnrbPA%_DCIiP5HRy9lKmZC|^gut0_uj zzU<4WZJdcsP_2Ut6TZSmM~qq7j(Pbws|sNc{KBR6FbhRPsQ19vfyvUO0D4zawQ4f8 z1+Vqa)Zd1IT-_YPWgPT*{VHj+7e1BBs#(^`kC;&(Q%BAnW!1;da6&MU#^eLw>=}Q06=%8xO`1=+P@(Uns|*i-S{vl=Wh`sPmVq z@(Q11axAiq{)QqpJlq3$oQy)C)*dW6445Y1n#t!00|%Q99@^{i;ZP5bZHr|53-*A7+)|(_WzkqM79=K{^$)5FqMQumSQpb+7(Ti(_1v*W2I7Odu zYb2^_)P)P>X*$aHlwLD`G<)dHu}K$yYn2tni^#F{!!yYiqQ>wo;1bp*o}|D~+?&8e zz@&KMV5WUbPmfIkINr*}!7j+g2^_HlFLm~Ve;!|AKf$*7$M>7q*g~DyIDTED4}5
Yz$Iykt)ZbQ9t zUOm4Je6j7$vrdRKWu8g zD!`$egVzmFUpH5Gn2Imx$d4;jfaC99OCJ&aafz1;=!lt~fv6_b(?L{0N=8cNh&s2Z zsHmE!{VkPi7cTv34*Ul?a@))6j*7IjkB^U(kE|5b(^2}2va+(Y%vtHPXHNrHoQCxe((3jO(@(8bmYkQf&Tpad7loxPJfN$4*NAN zV1m-$&q$wEBNM-qaZ8;He390|xa{|7*d1jr;eLzZ$AZf1mr`Xz`QKKfVPhthUh;h<3#X)a!q5WZvyKL;6IJWK* z{qsPkb`u|mq1~w&uYdXa$9cElZ3%A0J%wS#D_6?RY*EU${9+Q^3CSN%1qrbI)y2=g zYR}tN@3U|{;}+8FeLGo4CiRZ$|Gzul9D zZ4SqA-B)KFddm*F5!I7zugJ)oD54hrcH2M4JnmZ1tU&DUxpT$hyv(t<`N)48>{;|( zTZzjFb`p0_Z2P_W1CHaa<=1_6zzf9v7=CVyQ{vnnIDt9yQL$Gz>_8Gc(%L-q^Wzpu@S zqbYSpclmWWcOCnkKq=3GX}#x0Z+i6d_XRb$tReG{9omLt*d*h)MX&rT6KLGkILr=0 zZ_2U#&vaUh<9>4w!}j{nU3rC;b-XyD)N^DXJ<=1UiB1cOe z`)!b0uIyDtD@>06Od{J8Hvh9E68-17%B=eR9~BPmc_cm5l1@&fCn5r5zw#?if0f-y`Zp@aGnE^8$6 zjL6D;=vvF%W@^Hq8gpfM;<;voZXIhR9j(_posIOn8DGIPpD@Px8@Ep9{2Q;yZQF{z z*Yxa=F34}rX`-H-_=?G(an)q{=N*!WEBp3UhbUFflmX3uBD>Hc!b(Y%UXZQEdH{HW%$cb$+Y+Yx@FRmP@^Ii()7Rk0+=0W@A#PW`(|JQ!Ln#?Te08YaI#%Tme1=*Ml#;99W}ch$ zZ<|ySV6#)Q*JPGfa1r_t>*eg(ZSjq0TM6C)iyIlO^3>0D@dYhPUH88slzQ$1t)-*h zhw!RLxdZY_&aJmlj#{yRCJ~Uu0+ZJgWB=^^{oT8^&Ue3-D#~7wS^A(Xo-Qt>cFYn% zeS0!aW$1%p$4tN!F<|^~w|jZ>m{W8#LFR*r2_GPl-futrrnQnQU1X{_fP^U_yI<+` zeA-;ryZ5h2*P7c_cUwR~Zuqbgw`8ASzp?$4g^2Qn=B-toM3T)*VUQE+MxQ*Vp<(gr zm9miz$%lxF7NHokoB4$#VKT*4P=JcVFPC}sj4ma+HOrgm;QX3h9>SA+2H-v_3{yhh zfa7wHBZQBzvf2w51bB7!{I>O}5kulW3w;cTF&Hq7IsrZ>(E16e%3qG!ep*tw2fZ2`CMQVCwY2XF>3f2lQge1y(ZhapP|^d37ao z-&KF%-+B5TX3PF&j;(w7d~=pMJIQ2!i|IM?e2ikI^7zz1pI-)q2`w9Xc+iCwL{nQx zUuab=@J_n)i{Ib-?%t&v77Kmd1b^l)18?gwBp!Ao_whVYFq_HU9NhkpYoK%HT=eGj zLr0B=x{S+DtG|YH_ef@|j6LGn>n+$?)kzQuP-3{1=vDL6Y!o;RElM8FTd5>zuB!J6 zHk6Dj9cmzD*BQkGbY)w=k}6$UU#wNENR_@^Wn`nPa74p)FK1raWc*@JDwU4}9 z?A4PY(3zJlSTf*V*KKGD$!f?WMVm5juC=4UQ_hBeXz1Dm4SwB^sH=L@!_#Y45NPmK zUNrVSra-`ON5=dyCNloBE!mY{_|gL((77PDi=IMmnwg- ze56HWGF3Il?qgWVz^$j<^Q!ag6EF6?bVPkiYTG@ zfbg78I1P;rj0CQX>IjC}I?F{Eo(UVbog4}wyh0`U2f#5kNH=b@u;0jw!s;tT0f{7d zEoK6a3z&X`!z}VJI1E<>*OJ)GWQ^Ai!U8vOynes9#c9(DRYvNct--wn`Oe|=-9kQH(hyw>Xwk%@<rw&D+rv zpcXHy4>I0T`*cTLZgXc~+a#c3aShy>(hwWiIse5d=GZxB6?2@e1Qpi)F&1s*&A34x zGUCaoWEM?#)Wul7o`{n6r33dFm|q^~m|57;+Z-V%)NDIhdk}gh)(Uws;E|s2t%Y$g zOSxI0&(*2L1edF94(Tr5QDjdUNRX}r;u*K6oDzJ7hmY_-_vNVaC_jg;C!i6 z`P36H1{E1QQ%+@iQ4zEZF4nSbpVM*=Ich9!_;fBETHj6Z5|})2f(Jemh?2}((JNan zqm;KSti{w=!diNelReeB)SLMq)%$6F;i}BciPTWxJkZju1b4YM=4=gbrlCILV%hYv zYR7=jKoZJ-4HK0drNCNW$nYbBAm(1}7dxKXtbYxj&Q_$6SsE&H9A0AYAL0omkyvT;a;g?-t9+ z199`~+BmAPW|+>7Z$qKEc7o+oj9iEg4mgkJ%l% zz22J88n_lYF%1o}aLvorS=aZs>JuQ$mPRG3#Ki`!4~tBY91~yV;g{FENCQ5Z1m-9% zmx2uFgH_U+6814>k4$t8FM^+U-gN#6d02qVUhQlL zldjo`o>vz@1$q0E%9-K>p+a4#Of>5a{i9Q6N%dVZb5?b!ciWs@jVgIHyl}A0B~zYc zmaCBXsSj7yn{JZjtKDB@1+8e`k~@2+t{1L4U7@1_4B|o>tyQJI%TTZ_OIr$MTx&c< zen;kywdrJP5$Q=?D}!fI?-vbY+(raUy0;P1EwY^P}C zJG!tFfx(+IKyYq4Tx%ULqws05ic91Q)#jUK*R2q1#U9_9#Uk8{OcuUisA56-vWvcX zNdbn!ILyUTLd^3?dKZnp{(wk~NZ6doDPVmRpvefi`lA3U0m*zy>^Pp_u6!He{wdGH z4*(^7gi^*b-D8-xo3^@gRwq+WVK7BV^dnqvX^BvNn6dIcgy@t@_5tfK)%x7Z;cK1Z z;H&h$K^P9-2Rpw6%X!y4171g(FEwWGkfSb6cS9L%Yh-^OZM!$}Z-#>gqt=ra%Zihe zJ+G~`g{rsl*b)ZTXvUS`_?Sbs)8(z!PID%{uD>kzYi0A@;Eapph5n!~Luc%^8C6|l zMo?>ZW}+R*0@pBR5ttT~LDww4<&AUUri^wK{iS2iNXlsDTwOx8&LQ6pm%57ea6U!;J97(Wwje^c zCe_vBYj{2fQN1J5T9(UbL>Q_+ZGy!Eem72R^5--RddC>0Bt3Ao#7WntBq8oI88 zxS28Y{B}n#B?Hz9BDgOYaVi%{vWP2TP^795GpHGao_RNoUYw65R8V$h%nS#<1b{$) znnzI=X|fE}a%77kuvx8~Mx}FOgnhq2k`k5}$gcumGB;R z_9E^}k5uO$X%e$7@RcgDb9;27nMphNbw^QlWIEG5Ne)DYR`%37)d>|-O2Yu12_PHW zTLNYjx?@oY=DJfBxoJ5m{HU#9z54LSTwfVu05b4`jZ(Y-h}L)MpcI+@_?)JYpwAXVWrNkJaq;CT2VxOS^NgP-1;xC@03~T+ zph%Z-)R1+}jq-Rht&)c3gDB;>oapu4imv_Y(;*orSWi7#BkVKvVsnjpdCE_KX1j0q zw?o1w65~Axi$ffGH9g*HPxkIa$DhYwK_OntIoP|{r6f=e7CvVz!+)_eX_FWrD$lg6 zbV*-hhUCKzXv;*0f^sWsd&6p`RU*Kh^h5U&XNkk+_)=6d?3)d1%~aCfJnM}8YL7Np z`zgY=Jx`NY{=Ou%V)2@YPMt990ts1a=817QSQDHUu5{{j`PA8v<%!J0yn6H%Pf2~B z=g(WC1GXo@x^d2-N5*FD9v_Q!Uz{8_&vx_N>OH9J_`}A-I(;cJm63nbE<+v}y6zWe zQl!=`=ubzj4%$SASQBUi)O2l^JoPM);>eAMtAYtJD|Ltox{{3+&K5hNqS~h#g4wCi zZ+`*Lry603Cueq9oN4vh8*9x{f7HigJihBz_Gv|t&r=V0(6y642>E{hSi(9TOq;UF zWH~qcIsOzr1=zcHoX1wi=-U$4dTv#??A?yWZfmi7XY@dyUv~!@yW~M|rmslEJvlO| zA8ZXMFMbqqZKC$>5tavR`Bi2ni^gJ--yaTO94`BGM8uUh=-}rSFmzr`+lE!EpI@5Y zLOM&Wpt|dQqn8{GFrI$HP;h$MtT^2p)R?F-O8ei5Zo`$%JK>T!w-4z6gDf6u^>NxwV*uM<+z&a zvJ4VbjMh>%@zACGVL|vugqnGkk#IGwJpxfNW5-)KIuxf17zG1lHJDQ~YX6=B7S5bG;^~;u=Jh9_l2e61#$Y3*9;n z=)&__`O;|xThZoxF(t=o_<0;}dV(-gW`MDhHW4hIe3|9C)bY3x63g&%btVN_B4$Op ziVpI$uY}tv3g3L1Z!T5T3eA%--%dZ(B?l$|R+X7#-BoQ~D)=bD&9`dz2tLm$Q~-*~P~LveY1 z71`6V1SRFu6|?6zyW#!KM+Ys%SyP#t`*_CngwM)(j2Z{bysRlQHeuc@SM0Ql0L38Z ziF^mgyBoL~GzoxVS&_FIECmH|dVH10UQ%o2^`Tq4oS%&DWk09hibEo9xwyYsQ!c`8 zPV)LX7joQcMm(-zXuOP)(=14@O~C&P#saM)5(bCQm1LSxZ=z; z#VA>5MMJD9B-YyBZyLE8Us1D?uhLl#K0dgvpMQo4^(zE&_S-a7?UW z5YoKYt6*(g$HE#B$t>uO)GrHt3-CLRxaA<6tJ1H}xpbyivi#xIG4T(hGDS}Hw5v?i zJXKy=`BQH|GUFOgVtmXxed3T{{N~pcBbf`MWY>Y0EFsSUc~kG)c$w6IE!EaTkb#kL zyH(W*xLa3f!^72cJgb<;(($veqgaR+K`eYszo86mf8h_KIrzGj?X7K@w?=_Gu7R@~ zS@k{hCkSju`#-n_2Y}*v3{q5F8f=OkL&ZmjUlqj8hB8iL}+ zzy=E!2K~-c(HXE4{dYnz)-bkFaPil~b6xTy;LGBso@Wrd4nd>8OoJ(0uu8t!j z)~_YXtrz^v1Z4<_882qdcP(27u>f3FSknqrmjpm)|n zPS2XyJMN2EJ6=G`J}wHHWmuK_kBr`U1^(<7QOIKFG6_AfC^!xd7<=W|3mI>Wd`UVz zXIFe0iZ2Y%{zhGCLg7kw<2nn_ipru_^IP z(^J49u+}_5;Y#bz^PVe&>iT_MDYJ%CazH0XANLNwtf`BTTAiyE*O~+*(4f7GqOw6}xIkR-iJ; z5@tmK7xS>{5yM~7c9h+}S=DI(O1+s9C9Vn{nF`>tvb74al_;vo60?9b`4^)djgqdr z^XbAHje=6fz#XyIy+76-R$e_bGM?;X5^(eD&=x~HD&Bkc99Ln$n3ql0LKmJk7^_~f z^0HOYtD-Trb`ac~mX<;5hm50Om;50vS+>?|6G=uwO3TF!_^^2Y#m=Skm~bLRo;TC- z&d^1{P$Q&keFSpSkou|a|LdsjU_F;qArl%zD4)URDdz$Z zLRt6S$m<2?P|#S)NooKMMC-KHK~YZ7-cl_6IRo-(hJ(_)4o%6K?)5Z!w5erB7tu9m zW*ES4Q-O02#?gxS2|akMdRupPru|@+JM&#Xie>dY$Oq?|G~NZclj(H8*sy+SDlesc zp@j}w|0Fgs6e^;jv<51)^zb$84_Es8cYJ`MMvIo?G|-!72*GRnu-uvR(XFRajhLrChG4Mb zMpNhbLok77^!mo&dO8Ch@x{}bh)cc_*n-FHU*CF|Xcr8l$1hj@-lq_;${WPb{eyx{}S>l>xY)pFM}*0ft-ZC+i`v2Y=mIvi3=2;oIb)b84% zyuY4ZyXe^{Ld{5~h$2PScfl3if;)xbGs^;|z?knrBi`Ayu%p#_hQ@+lY)P_XZ zUQ|d_SsLu#Rb#UlC}=mZ?y>4+cR)96iJERIV@#u#>N8<2%F?-A{$;m_o-`3CC0;z& zNp6F075GbP9X^;Z4T7cXb`_Qa>!iwcjrf^=`e4 z|1O@uf8WZ7LG8#?2d&iG`_UmmfD<7_geu@Oo!tUc(i^nBBmKg8JYc2_VY~lkm%>ZZ z1PFo>`!-Fz%_GRGy7<1Z<4G$^WDP)C#I_HUu+g>?p;~YqE178AbjZ_FCaO87M20J1 zt-1@9^ir2rP~g6`_pihH#&HLkND( zUqX{c2H;gjgNi{uTq2%k<2SYpQWu+Xne~w*H9iiDzVGwyY#k69dhu6M)lIV7w zMa@~sxX%WaJ397WF-|oeM4vQh80zF2T#XObZdoXXH|FdC)ND&h+4}m8=9Bz*4#R;X zXW|-MpcQlI!mr}$MEs3p;~i+J%9W1A(XQ?(7oohg=7i$Q#D%cbVMEO z?e?qPc^nsO;;vU_DPR_OGlSm{wKp(QuSC~x&s5oH$J15_td+4`W~^Df6mNh(dH=wC ziu}Q6wSNB1yoF097U^y!J^Ew4%n*SPS3SF2#V_wM$wcG z7wd)iPD#t`?fdKI)rFgFC4#h-6B`JRLCgK+%c_);GrTA1S1FJEk`@dG;G}`vU36jy zQko%L;9folgpHP{7i5fm#BYoJh%OMtWeOf#BLXRt)B(tBXRE@nRjQdxR}UXnFrIY4 zB+Mj5&b$s&nKh>rMV;$TCsM4uZowamA^COTL?9~YSBZL@9q9MD#`C5OyiO?Svd-y( zK$^BH7J_)|=N=UfNRF`~7!EP*Qr^wV66tTulTsZ2 z)=FjW!}#(0RJBVpXNs63Ykn!75ihMO3k=7{YW$M}5?sp%!i(r5E`qt@E@8EPV@Xs* z*trXdiq1J9m>uuNtpgMv%B-qd2Ili-at_jh%_-JXhhP?F2;xGWg4Jj7Pi?UlZ%)QF zSd0$t(m7SCOXJZr0=6gL;a1k*gM+j(8Vc;S^!|4Lo&A#YL;~RVIbSrSJ%d=R12a9^ z$^dWosU)vzkLiNMz=l@hn1w6o+cQ5h8g)KY#M{8!eeUF6}j-`)}UB~vVJfz_C3!1wF zQCuQ{C-6b3yj|11?oSsk;dfPaQZ4x0g93Z_U2q;tU6qOI#Uon8hX8^IDGm_h+XCkzPIZ-* zQ5Xl!7us{x=ANFUMv5G2>mg1ikb9MdyjONx^_hPB?ju_BITDAhMEr>3%Vcwp>~dnU zIJ9fzKNs?=ZA*TbtzhR0ewpHQl>SU0>~)oq;R)j+$NMcPW3h9u`0rs5&@u(9`Aqty zYGP4@E%vF{5;uB2rcRg^S;*kAnI6Pw0Z6Z6A`yyk92SnX@;v8H?<04=L#9hA^aW;n zHDCze0%$k{JTxl6aMa-)M7EK>!}0uN1;Wz*!AZZq7RpolmnqBuoi-l|SBmOh7JQOV;UG3B@i92|xPZLtI6 zT1lw8PX5c&3-eE00dEBMxDxeHWi0rd-t2(<4iy`8PzSpK_G(p@Tno&<+<4LDpLT1s!Dg1H#3>@deJJ3jV zbXgLO{~VuF;a)%9I6m#Ox`acv&vadNBTE`!iZRxqXLE`VPA-Y*oElJ4yvAcBMOdr` zoSqdo+iuK9ZjCj+Raz@PnzX!-T(tR#<$9%4?8o(F`mPh@N;|t+Ah2Rj*+7l8b#P{N_+uhekOE z%4rHu@;qX3kQQWXfcxk?40zdr;1_!Ey3p%o_`9K2Gp7E-0JkKOwvq89!NKn-8f?L@ zENlTc(E1n6h|8hE{`M4T+yis`5>2o@Rz>Wg;m3QJQ=fn8@=G$&{QlC}$*kfVbCYs(mGZ;YC5wQ-e$4;%rj}uB)Ybn3tdzEzjKnIhnlcpi~`5WNJGyFDB{D$`F zaU?T{yxx6jb26*tYlIpeedR-0F`3Co6}_HYp|#?iW7F>V)wRV*Q$ktqvFQRTd+x%N zx{qcp))mrZ8BMAFDHX2k@ioBtW}CT%_#)XYDKjcMcxlCGv%!iBCLgZd1>M>Er)S+LM&q8z_QH&rCZ|( zOWjtg)9cevZ-t!| zUr*XW>s0}#*kTQ90)FIH$iTKYU8(AnCs~5O8eq$|d%lm3-PvtZ&QKX-T|E^y$$urR z+i*3HS(7#`Y<9rUe{RCWU08lX-5yJl8gWp z!4nscx@9VK1ahx(Z895uNCC9`&29pljngK$>pc&=s@WP=LFTP2(J{9R>%wa8@V5fo z%isOy9tHufE9>J-#px1iL|MG_AIQwvMM596`XLHTTa@x4!ypZKIsenjQEO8yW)Y}r2~MJ+hNqu2N?(B{ zx#UgCO_KzGn4>s#X#}75bRFVB+({}780pGwbhCqtL;yU9D{c+9o2Vp7OX11N|?mDbU1>+N(On$y*|nK4(>*2#Wqa?4Dp(X5;SXkLh8*V(6G?wVnve+33LF0ouh30t_*AFz3>+j%l*D6+x zWx6m!pr8KJQckSxB#?vyN3@}r^TPHHTibSqV4SlCM@Gl59F$^pD`<3eA289Q_8TZi zsjMVoP7=IPF8&|u>|`?PIzJJ*Gbw=YgxIrFvwQB$8m&ullr@i7dKwSev@TyseQ zvrMJQ(>%*&;w6cIUYxJB^6vGEjT*lo=-d;48{pcx?GUz3d;AQwY=Ks!NmePDpdn_o zUj{@y>z>ah2BG!^vglic+_OqwZ<&_4ph@gElD3zL4q%K7f*Gg$zOJbSGYlHd`s>oZ zN0%PrkC%x|7If!=X^FBO0b>ZCfGz{RYE%07o7HGspu=#;X6!VU1SFE-Yye-(wdD!N z#|xEVN7oNzlj|?%<(}Q8YrMz$r~9Hw`bXwU>-rH{lK7z|TWo;<(d=rN2^15EA3hUz z7MMLXy&X_da$Os zm^=h5e@)OObGuZ?kocC$;Rnh_iK8;+0>Sn5#>V zKrV`Hc08cHB5Oj3d|_#^RK;N!ec$xwo?h7g!xg#rH>4MP<&1t@jD#+Dh0|9gDS$sVtln3=<$q#x*SLMZhwtJCvg` z=f1G6zX<&{qD>4n2OKPOi$KhV3@fl+Fk14==2-(WHNvp z_|hPge$YichT4zzw^>sc{(fIn*;cj4Zfi%+*2DQ-SrH!aw{3peQT@vet8Q`bMzv0M!L~v%jQrsVGV_78(0EcTg+V26=IK?4bJ_ z2%v52{KrpVH(N_irPIi7@9IM80!-{(*wGSg1um`W0_4|)!LS2co4 zH7Y`=x1s{3la>b^30Mr3lv~_iu{2V2y6kvZgesw}s`Ve%vl z2>Lxu=*}uXL7UIiy2zW{QVYo`ys5JIT!dFn53*tiI1@o6bY16r)2W{cvvCug-%az7 zA2~`yb#+b-!=Zo!5mlYTxmwR~KJBlDu7uifqsy8v0fzW$tclqKv54=fG!Q@pLaz^n z^Jw35acmO3{Z-cDhOhD5^*_BTs5oF>Q@HNgxV+=sWOzQA$%UpOf=bGar;F{vnS{co z4xO$%mJj9R^X);iHDP$!!S88=*?AQ?-sE=YCeStH8ZpvbzfQqgOPzGkJo5Fp_(7yo z^y3LD>RR%RWOeYz+53EuD_X+vrsw0=19Ihw4Sa;SF=xHApw6?PDkEHK9D^q{aIsyB z7`R;yj5%$?YS$7%dA8zSoH{o1!@_D^iY2Tc6M`>#0J*=s2hUz;h@_0$I(DRY5f74Q zA5QKH!}-Ngfh4C6cnhMYr9Tb@*P&fc#@Qn-YHbs{F4v*-)SL_-K@z<;f$VB$0ssS_}msYWxzZELAx zI@`p_G8d9ru3&`ZK?j@~P0xA|$a(~f`-w0BmRDqZhi491yUiT%BqL}=gr}2g(#W}| zhCD-qYV;|m7Ul6pE%(in&pbsS2@0n{>$76hWXsAiE@5BM(c+$43AsxImOjpKVp|RG z5A-xGu058iSven#+h0+0qG%=Q*`D;8KXNO#?%28&z2yWdK-L-v1c4Yvy_53`#DZM# zrAuX7(U&9~!M`O-r`!i>4YI;X0NrlQoQV$55A)is8)npk{3|*5NAyBtTktjuzztYf zpP){=fZ`l`mG(jTR+e4(xUGbyFoNDZ5lib=K4Mta`RMc1h2M%DeiRaX26A>s8-<*d zj>U1?6#rh?;Cl|(Bf&wJ0Hf#F_zi^Cpf~Z&SmGYt2htY5mrhiq0Lj_!!D&Dw_wB;{ zZ<&K|Ei#0{_g`Ep#%b7<0cK7WOf(SGpS%07RC&>cBhXx^z$1);u?fUI%$25}5!t^A zIAnwYV`#`CZg&V~cK5#*eH=LdJ-`0=KU97E7sU}krGtqK|9>na0g5pGseGgAf4EfS zzo>)w@6{%3OzT&=?9U9=|K*h=|9e&o$jpiVnL+h0G9&F2z@aJsT$S`$BJR68tvWv5 zv$rtnxNg?<)W?4oO8jwPtC4b86L61g1<`H+^;w@yf0rb?MB4~}_%qw~|GL!bf6x9A z+KL`riUCNl)#X3Y@fdKYFE>|ps=@Bd|65%cTX6ecV7mip;{UOB?f;+@dw(COJi6Jt zC1JWveSH(^LM(^jgBiJBS>S(DMI1nOINR?3$)dUcJuCS?S_`-_tsk)EyC~oWO3VJS z`0;;_B>ZP#_fNH@8`Js&%m$waAmIjh`bm!eZu#(r$oT(DWW;NoO=n|M<^BFFfDL^3 zCzXMN|FWKagCYMU)rJoH1JnbtvJGMXBlX}1EZz|If9{d3yG|V45E*}xgL|8G5r7fz)P7M#Ab!$;<6L%U zl;hai^chHgm}UG!65HRN+b&`qfr#s8`|{$W^K9=s@RC{Y(%U6Da7|hgpG~Gny%fR_Q%g;fB%u}&d%PtxjKJ0GlP7J z3x4_D6TGNt{_C64TRW~^$l0r^FlA+Auy=3yqpjRe@M*Tt&jwekq#g|*7C87K9nU)W zI4;{O5c*r;W9iNHH2YXtmY)9DWLLJS9tnlGkh#=tIT+MDA2b+0{~&0^%zpLMm(RO3 z*w{IC{p~H~Fvr-DxF@oFx*tI&4&P5{k6*V}_e(&UF z9T^9W6G#4~)sIg2zc+oe(R71Mf3nmDOZ?hi8$0I@d9lF~8!Yjg&e>pz4VKtoi4DlL zp(Hny*SISTxYLJ4>TM_|SX!Vc~IW#Wi}Ho^RiUpb;m z9fDa+;@hQ_2&c+E-8&2Xyf-@YUQyOI#&dJqe2Je%&BaNp4c5s=eNJg723Jm2Rkkb7d~H5=LTeSkGU9SrXuNxjtML zla9A6_~`KXAgskF1-dsq&KMn0s*9iUK0X=ko&TUThF=o z`_BDy{(b+G_g!nv%rnnCGb^cV5PD`2lW;d@F;Fn#!iVOQ`ZMjVG$ZzSN05PwO5R7q zm?LSV#{q(}>yE9Z!pZkf6Eg;XEnfodRiT%pGr4)|9&ynG`#j-FfGC{Q6l!ly!~R~u zcMFPaRB@C>eSVgcuGO&FWsT1A3$iDrE@R}|Q1s?e+aOLxSAaxYxu4cT$(*6h_wQm? z`UT+$Qos{@A&+ZxI|b=}eKA*h6V@qs_AeDs=EoG%tCvpf=T5A@D?^^Gl#$6Emms#L z7cX=mqGM5$UAlOkDo$7Z(MgF7X}z^MSYR|?#@84rdkGs>Q2H%Gg*MFRoraIp)sxd9 zWw0Nky%u+`CppG=85-6Q0m5Sw;d@F4YA4S~E1@Kg?e7!so7kQjj&b&gL&+p<@64R| zonZCbE+6uyD2w%SVhuah{cm8#sah*uK5P`rEkcR^dV)v@F|*W&$8Izn9c(y!V*RO} z+V}XxpJqr-cHx=B9;cuvd6t&2X}Zs8(z-`DRYggzCp|@kX!=TCjm4xIbFovu$h%gg zLas;KNr=cF_Cyx@7!VKo^?i3K3WB(xk2_H1H|v$u20s3P-|WHd6MqRmeHwf1%4<^^ zf_FqwRxbzADaBv-rX>?+NvEVkM|i5K^jn`Nq>S-0C+EPHMO1X`DGPtu57T8c5^G%g z>)weeuF$2iMnE1f(4DOFN$%VH<1-PxrZ4nYE@@K~N&1HqE0Yp02AZaQ6n%7ZH0oT_ zd8`ll%~Z($v&2(G|2f4|kpKANf5PQIpz|MG{9ns1(j7!1XKTics;rPS>~_sT$x)X`^w9vX=pI|hyQU;Wo_&YhjF|4W{B*Q@u|ilz=niacq6SK*68*hZ zfjsju;H;JjzCkBEC|vZk_Aw7~%&V~^*P!<0q-5+S5DI4J$6DG6!;?zNAYw)4>HMGFiiO(rhh6C>iyM`8Y8iO)E|W_q?=p}6<6UfWY;M>Ts5hT^3;2~F zrYK?gC-uADVU7L2;=3$IBH#hQN^Ndv(}1_N-9X3 znC>$XU_-4RPc+Y+CL?;l7CpkBM@Ip^$(`AJk9dIKvb|@1`;eMHQGfVtBXrlKPO#4c z8(-A;5LFIP52bkktWQ%_@2LhcqXHY&&Qr$wRShU-0+;Ln3&UM|`}HcpIY?{&i5dvk zh1*V4sgh9+WKYH+!|%J0-lRu<(po^^qdH zKR@4tysX+BYv#o8c)Q?_V?*qC9dhDN6^R)ivKigZs#!8+ouTuiRstpOI8~fw%=_!VUqXuM5BtCOl8s`N`lK|${q>DAUp=L(JQ)wNqF>6Wb z65%U^6&F?RJWH(pE3p4OC3YhDI@GNk3&`gfzRW6)1!g|-%g;<}LpykywuH77sRGD2LD<`_%Wse5yztqSjb2i- zC?&{plLgYsCk@Kns=6!pPMVXb#rG<#Nu#8%s_prz+bR~Y>DQ2x-C5ZE_QUY=kO0ce z)tU2ZI_Emc1yukq4ko6M**v+A+2mbMtzeO>5(7O$1eA(g{GGf$}fS=-v+*a=?n9t*kO6bxgj&}J!>HU zqyo^mty9WDoTu}3SDdk#H50Y$m@JG=k?Q^nfUJur_>A=Xa~WR?y+WDZhRLzrIL3&9 zA6IV?$r3Y~@N3P5>i`8AC*8@HLNCrWMsF=(URdJAj#f(7u`N z&AA>f4Ez(9<`33>kK@2Wa%H~k+F`bH5;thubw%Z^E%bU-nGptm?O(xcEs&AFs%_ zA&}*jf$e<$5bFqePTt}73~W3DA^WEMKJm{)kVUf{=R#X=F{km4B*7)2nqI1kV+0gC z#tHMfr&h$BJZivyoAx2;SBe)4A&f308k-j-shd%FF$-|U4|~!>2+xmMddTv5f>)|1RC<)lMbE3wNyVC}I86$H;YVrxls{-K%9Xny*VMQn3d9;sTF>Mm>U57u zu4!^c4@-rq0ND1kaU4&lavTwvMucNz`hARPLBv5g_Ipce5v07zx_na-2l0H+O}7TE z_%OgxE&&PgXKT78xnP^)8OWDZV!j4S( z&;Xq4a}pu8HGao7fV1*1o;JvQ?lZbT<*nkqn^;`bEq4mi37kM~0iUN_nl5n=32*=k z9uo3?sd8Zgocvs_x9PnMWqh7(#vdFZIccolfZJ-5!Qx`>GpU^+9$+&*pxvca&LY*2zxHub zn>|!S7c>d>#!5tgW<7Xx3SxSa9|u|fu$KZ!gxFtA=|kw^K>ytbCQW%Y1@Em!sst(iwc}Jm#$E?ovWdSotF-&{K`}X0a$3967`d zH5E`(X{!!D4t73ge*;)&hBU`D!lHaeFZemz_W2z4E+H=zs=Q-}kxh6^h-ZUdyP%wEOgcywXyr`EI{_fK;!O&z?1i_F~}tF1oU!MI%w zz#V;>Hsk42#b1G=$tG4Ok4H3>& z2bIq0;FtclZ-lcDxE)RPKiuv#@iyh?o10FuV&3jG&)-Vt0S=p4>;CyF5%n#AcrYo^ z=RZLOaT&nO8o#mxchz2WG!Z4zA>gg~A9#CQ?UWD3I?02ZLUe$tDEAzIQwiB=*U9hG zzdNccqPI|L(zFR{v5?uEAvYyt>ZI3y?#h>_AF#2a=>x7y*TXFV+vVD_a0N>HQ3%Eu zb6K>X_RS>~57}cQw&Wq0>yISOrlu*)2e$(__R@HbOuw@ZkmmUAP^@=@uw&@N2{etH8r&^@EKS`VF<%Y&f03a&51 zGE~H0-2i|&j<5*z|6vg=w)2BwU#fPN>I3R6J6Xgu^G!G*BV;PE$49-#c-v6)FFXfBoL*@rawaFc=Nw?>NH}A1Br&bwz3ws zGb>XqZy;5Nh%=w>*@ zzHO2EK&z9UW&YrU!y4!_UokKwc!luwlz;PeO3k0kH>g8NX=`L}xfjbv8a^1V-+w$% z;TV|N!rpTuiR=CQXu2m^1ej+2H)nbA_QlP`LRWMRj@Kf5+iN&a@wwLLbu&e9|J7!q!lsouUiqHv!rdl z<<@?X(Xy1*f*UK)M%w4ag!C+v-zSI})PL~a{~m&55cTHVSVOmZ8c)1yfVd`l`P#y;t*9m}0?m`uMPMyiOu(x>36dS8n;zJ)yV5g`;$ z{p$m`LeG}e`MTM0RO?q6dEsBj+gYl)I`$HEZBMhf1R1$B6@tIl{A(NC;Pj zvuXvwpgjNj!*tG-n z3VD6M<-W!>Dmyy+TLh$?_%}~K#;pHzqyBe#dJYRD4F~IOMHMw#7(6{GF zgEf&W5~iP%K%3tp&gyQImYu!7;-vGYg25 z9RW;sF*`5p8bx(3=~dX^ zX2s~Lr+|0mbzZ0YFPBR)X{oRo@e3`@BhBK?VsaX zv7L@327uTc34aP8^qij&66oW9)KpPNB4mmyL|=Rnn>5>iilCbRdS*A$SmIM7{zwnu zwPIu8`u*c}kH}Q($dp?KwOO|xrfos!(M%ZVDBOJ^UTz`9bG_7S!U&9G7rQxFztgJ` zgCh~|->O?k1t0ym7iR>Af$)c2@XXfysd{!B1%9pb7r4zdf&D&aN@+gnWwVhGKxfO~ zSKBv96cUGa+sU?nRq#YfiTi^YU>6ZgcD1r8J_}bi-b0Mu zhdK^>MJk)NF;~T^Fo#7g4dy83@J{*-H^c5CoG$)B?biZXu1(=LWvmB-dGD(Vj~e4f>(kN>>SufF%ntYD=KW|vXNv{( z%;uxb@(U}&aTvIE<>)8bercOlm{C_@RcJUe1>EoX@F&O7*0TBHT4a84Wr8&n{mkL} zxj^}{tXLYUaWE9qZ~%p@+C@zH;FJ-m_t-0ysC2_*8T5uy!3vF1rIRl3xq{}i#Ef+S zFK9)4yUI%N%l}*26yW%RhXgL1^+43`-@4~91bu(0@d%qg0kU+mjyG&7GlF}6GhY7E zEZ%-WRf=8@IpNqFL9+&LS+IC9O~TNjrA-^u0|NdJel8-I?_JN}HzAlI2QoPop9^99 zoIKtbwps#f5QY0-JED?|mzQ!%iPj6Gj`pJwl|XWpe8fz$^9YC0 z-YHW3TK8fzRzL@;t8eVgqfe#JdDiJ0g zyW|WO%)N~Ew$FJ{3qf6cTYjYvSH7HuwB80#s}NRiP1hxNCs!S(eo^g_(Ru&QX!2~7iwtz_%V=i$c&2=$Qd}sExf4EtbH0>eEDE+R@D@~=BhAZXs0*CImL!5R!&)*T9D(9Zf{c}ehFaN z%c~cE%^saTDKeC9)cq>D0DJ(Dpssw(9PHlgDP~k6-)1QGm5I)~PtpZ*CfsW!Ry=Ot zZhioDBM)-csUQ;p_3TvOF1%^yex@w-&BB=I3bDYarlB(#tWhj-Xv#}1hN|Kfsn+E) zr1#c>`R0STi)q@Dg9_*9fe!)6;yROv-Ny@;mo94}-K+SQSGwcNOuw(WcJ7~Xw3GFE zy?Ul}1~jWR>D6T~IzJ?^+Sb8*BcMwdZav=z+KjgLLTRxG{vCZY ze2J-WYBnq+_Ezwn^NFcWLK?BW%=6uYs%TiRE5engy;^MaGD)(-pO8>_4PLu!RE(%+0{RuKXJHktV<_+L(DWo27xA$e~=XfUyiTOujI*2eO;Ulvl~x z4n6QM>mB^8r{#MZ@fJ(8Z&V1^QKXwG{vK-TEH61uyib(dhDL(#5fu@mcKNC0P#vCq zApAr5;pz>9xLNR}#K4spfw%#&#mF;jf`sH-QZ`@GmKyFN*L{UpqLv>(fRPq0Z6op8 zc5rceRDgy{f~aG50d)Pbi_7Y>2FcwCIYaL?^r#r)xX_Mmq+)^;c5!yp+5iJ}MQkE* zs6o}zUJs_W7`QJ&J=d>(rO+F0Q&)JWe)FLVeWMZ(Z{L)Ny#S>kiJV}NX@wImUtawW zb2#^o{5YxKQuR?zao^Q(x9^sDUsb-eyi!nBDIs<=r8nDoMQMr0)ng?&**C4D7mQ4H zod?&=kt#J|2XjaTiEP(%n`8jcJDUAHPIscm!yT<0n6hnS2|DdC)FsmeI%V~UWpztM1zQaMFnv#h)i`Y(GkVdb@3Sv= zKKXl{`4CpUb_u*`V<5sACpL@H$h3-8={MNlIPg%NV6siAcHMkH*3QmgG|dO&U;XMw z+cjppoylNwCY6l)VqG+!#Rrbb-GG(iXi1kTp;6^MF~^ZRI-beY%?_KlJX$onl6AWd zG_r|<54F8LW|s2{FD1qne*w<9{UdYeF;(KI3<`K1CLz|;DEZXRV(86RN@b3#-~xc0 zG+twA(!9nKV`gwnTl-FhvYvFKT?z5p^E+NSA@Y&84pfuN$Z$t>b_>i^uYsIY+L5g{ zu58*q{h$0 zao4{dyKLnElf-oXMG=Nvwe_POi4AN!5+@gL(xf>jKt~S9a(`W{Kk3Dvf{^~In%Dj? z(cyL%UumoE%KX(bsnhET`MiBKzH-_Uzf!2RfaRPd;=*jg!A@k6?*K`$l9x)1fy;v`NqBG$u(sS|}f^o|eS%DI$ z&7_=ZSy+W%^@{Z7!MdyQ&r#ieW6K}TBJH9`nh)y=PH(r#>fhwAYN-h{9O?4bZ=TmN z{U~eNFPrFwH`0uG{*q#>cui^>GX?JA-&ih*7clSW1NCi(xsUXL6mxFCg9B9hmkLfJ zJE{HbKJ;!+sp8y%fQjKG?&56{jj#_)H&%-3RC+7_|0)NLFDvUdb0H##ZqfDOXL?$- z&TFe-Oj+9U$_a;g;OJ2APf51bL+Xki>4Q`+*zUQtD?2RcAm>&!U@MN>?~i!g`lK*k zFTZ?ZlJ(={K~mK^f#uMPx44U{KP}q4k2UbgDoVKz7}jg{*-}6oYV{gZL1Tm0)_kl3 zj#}n?+u#5iNJz+3)kvPoaTW8n>}Fc=M=GGTsLkavR`B7|an>PUL ziK4di$s@Xs%IMA>lq#cxArD7Y}FxdtBtj^ zlZ!iWWry^SJ};XwiIdwYoXqT(nW|2}jvQ-HZg`h_RuxVH7$~JX5paX5WGsoUX>N}bm6VPz>LujaICvB3i%`MMsBPlk^K1_+!*;4zadDdkHUB(z^j}TIg zM%-@VYOP*k(wlV4vf0=CS@2{xb)y=q@2RcJb4OJQ?etUQ6rA-FL=zc-cS7{-n_8TUJO{Itv%rm!EA`$owx7Kz z#F=4ru?1hgZM(QYyTn=--H}F@;i~CmP2Zno0tGVql0A9@1WXEx9Sb2J6W(?o=Q(Ol z`*|%`I|*XV%sIYt+)}Z|X79)7utY7ZcSMR|yr%WQgKq%lB1SER;|Ba0u{WJZUov_4 zpP?N!TCY7|5f>M3jpFSVc~>`S-^N@SD>CK5%|Tw;6m>NJCU(U0;#tUQLq$E#1>U*& zLZ&&trvZQDX;Id9^uCJrmJMt7V18RG=+Jm|M6&Ms<%{mJYy!uk z@ZiwZ*U?l%m@ti9%b$Xd1z*(HWi+^)54;Chi$XVx62w3+Bz_1cJ{2L((;`S(cYm|t z%(tO7PA$>eT`jTthWWFwHy?Xh>;vvpj=mIyd!vW79lkyYq(A#yMKY_nU~V!1Rw&mu zx+~ulRkXQTz2#uNalxTAky3a>Z^#2Jrdv7g5or8uj}iKkD=t@cU}dPr6CqYq);oT0 z8zG)$mOmm|g&H$fz~oj*xNN-nP`@|uefkDvL#5@gx^4A3qmD6zq?Qs&_U6wKw;W-<&@-7O!>5qCoBNCLXlBzB;b-2^f2{7@x)kDyj z*N*c1V4oDXXxxCe+4y(X~#V{diGn>FDTi zhp)zN?#Y!m2g#x)@=FfMC@xNBCM&p2RZPu7f>p25&eRkbZ@_X{@^MkkLhW}wnhNFw zyVCcoB?mPDRS;JO=&}Vpurj(&*>=a!+inEbJSm!ad`3 ze_noZa#=^S32VE%dWwO3YEVx44_eUSG~YmzY^;IH6&?E~lC?aNE&=nNh$SOsO!Hmp zv=MzrbdugK<@$80Kcfp*#@4Uj0!Ubjwl?y#Df#h@%5$xzK+s|!GfG*94D$H6*Vr$_ zNjCI^1jq$g<8bzv=k<$Ei_udRrpD0|DI?)k+a&gkYCPpX9{Paz;@SXBNz;(P3JZU4 z`APcM8(~V}u2Y^y*(%A)Q!bbpIo1wYhLjB>cC&oXg}08To70Ie%g{0Pa&2MMNa!w> zX?q~}0#0u}TLq_Cu958*#z7q_W_x&JphTm}9zNZZSlsy2`CTk|&~a-!`glC5lr*C@ zp1z$yX-VTXI_#B2Dk|)u#~YNyPPayR9vZlx;pmj$iujqTQGUJR`GaB!{7P}7X~X_P ze#^6hpi_jpY2uHOJI;$g-ej%UxSFc;^hzS{jD2cZDE+Cx?NQYOy~Ais&E%uk>tRsq z6eXYv5=sKMCA)S}aO@suc9zMy&R+4V2sBJj6#>S3yC1&in8z{&e7BVuaz+=Ihk=B7 zM?V}-nmq*Qpw{SXP0>iA`Lo%mjw(!85Gc;feZz9$njcvQP%AO-cyBh9I8wHWAJhE-L)-?Ef+rw zuh*7@-HJ<)^sJol?(^Q>Y@UUJCcGI#6?46#&?9e68jo^1=CqB1 zd6xU3!A5Py3q^HR6bNiF_M(yZ>maGk`4RZz_GDMY1sajGwyORBrY9I%15+Ft7HlI2 z^Z^|R6}a9XO#33`>m1uKNldO|&#i{Nf;)|e4l}!AD#NWkdUM1oGnBH3#kcdGt0j6L zE1m=6ITpuL7Yiflw&aK0YQ4q^#o-8_HaxCw2aFg4!<@-{9Q<}^yiuw;#ZTI9bIu`Ga|3%^xi>b*?nO(o~P}_=>#bqv*{}Zr*6-$|+v+x=b3gUIhw)-ut+VO9M3~Ev@Ye zdrw|ndn-+HJdJlM+Tgm{X@~=G>Kc}Y{T7W8qjg%7R78cd^!M_TJ62RvpOm^ zp5wN#)Ry($bvQ~z9!GAFvvgj$Ise^Daen{)jP+<>^yqz$GNmue6$aKr{o*~bT4YJd zS`dBc%A}%-M7pbWMJ=fYb~DC`%zzcHtv<6ZZWmcth2XL{w?ilW^%pv&9*3&E%fnF* zwa2F*KZ!6QSLdB?_kFZ$*somd^iV{wGZ?Tb@{S2^)QXL0f;ZH6r|%uqb9+#g4~i`& zaMwsc>CSi2eZGF|%|p*Mg$9ZCeRIuIJPI=JW?y=442esXf>gJ}z!&swoW`;kIw54M zd>|Yu(z>BVShTCSdiCWP7+*Ql5aF9^|ANA<-01Gi$H14WP2881EGA(FAeL9rn@^9p zRHcrdtXgzBQ)esSBqSR+SeT3kMs>5+BhhrPl?VZR=O5wY9uX(Qj~tTFD+l5D*v>F1 z>~pE3y$kVeOpA`gT7ZJDbI@2iqHE=DB3(E`_5@2@r+&*jVhX4U`mQ{mtm`)7n}OvZ zfwQXJHrlX|vMQ{|wyti%z7(&@T1|8Y`Y_1=O&A#C9ilwV9nKe5x-!q*h;F^9qhzXCo$Et(Y%nJ?GDwHKe(JP;ZZ$?ok#o|wP71dw(BJ0)=n*DX7RlwdO`0&{<<-;XPugz4$?CR@gcko+ z$!x*((}#|8o6WQ#g^B>)Ef92Cfgk_qv?uDwKfQVDi$u|s*%xzU+IY9yZ(N`hgWlT` z-Pk=ca~$blP_p=u35iVJrq(S|tx1G2MM&q26KjYe7OW69z8k9X*IB(b(*bra5dRa} zCqiRh;|r9(57-T|%u=__m4Pu6GlH@)@=r$u7%%-VBLyMA$S;4E_PU@=0{%2dQ`5?2 zm7P8iSnUJUXEB5us3GvdJ;9f-NnW2&Vhf)a3pTyg`OS*oQflua>IJvV#;VpJ{W?bn z+8m8H75OTn@C{MM3S{Q2ZcQzps``dsacT3LrJlb$lpe+X#n;&#=vbw6#wIwvrFrpR zt_NVR#alVhR4W|w2);&?zP#!@x6`P=tL|`RVKgaqp8$O=Jr8Hxz@*Z_YQ}e{a)O}F z?zm@Tl3Apo4}$fPJ_9g~=){KJWfq%KVBi$ICmCtJ)G$H0E*)7oeXnUzdW8& zKk1tNn(cU*LHF5|-+5w-0|`{0KbPp$fC)b(Yqp(JLPq@PnW(A@sXxVEtu_x-=&cWH zvx4_*-DI`Xjq|dzAG^R!?A&KVMEtnE8|nc~JIte>&SuHZBbS)1y^je?7e)55`fUGe z>K^g$onV_p@sctbb_7nk7G-xpC^=qh$4Yste$=z8lN6ppV6|_@bt3J*ZM7-H_A+My zYROCkUk!?~235-at+e48~|}*0RUHYAu&MC7+`*U3TP7m6EmX6Fykb_bvw* zK__tOH+(imut>cDs2AR}y_=bNyHHzZG1w4(NzXR1%!vAnU^`{@!=@p}K1n{Zt4`0q z`J~^!m7VQFCE#~v}5QdJ;nW~l;n}+>tpt7km1!y@X z5((Jdf6~Q<2Zv3$*~QnGR1a{&0YI4Do{u&1hm+dzHoQ)|>af{(<`V;D_O^G5x8?LZ z_(oKu$~uGBqdj1pM}3nNqDctTh5uolLw;pdUEY{UOzE&NysJP5)aG(`v%W0Lpssmt z#jfFFh=|2Gy=;H(;_9JBX5Q!Fe77lM7(9nPpnHddrF%bv1M@FYW}_^;Rtl=#RNHR8 zijLNf87Xv)dDRmWW1ix!lH#_VYy5OI+L){L!RgaTfqpv(X+Ah5Tnc&HKkPMEOK+#j#vq zpVF|@56w^VP%r`;HW)7hs)J$`i=+vB>>)tP6ajJuA3IJ6`Ib5O3f{|J7H?gCZb4TipE4wN_b%LxU8{mWvt_+|M_84L zHjIfcSqvE<2cU#ujLxeo0HP`%tlo$hv?xtlmGs{Lh-%21D2xTzK>Ki=N&x^{eTN-prnTf)_8KsE##GXus$UUWnu>S=2H z^j9|l(Jh8kBfEWKr~3wXVf90KzX~$=B~KjS$8$h!8w;o%DhWK$^2N1x{Y_)RHhm7j zuq-!Wo5k8J)QWjY0RvZ0r_1^P?%q+{b+7@R z-f*rMK2qJCaMX?mH|%%rajq&PoA;&g;0>&l`F}U;fjd%J#2TDMQd+=0|2Bea%pAMM z;|%ibSKWKM>#XRs`u!2{?RZRVB=EBbPh)iRatq1_f=Zir;q59suHmu_HZN% zgXbV*tyq9I%z2HyO5`!=;$%|`A!4jf5i(9Y1g?OWwp>c=094w~t<9gclUY6X&WX-; z@0}{5;wcZ8xMv#$v`p&J<04FWZaQbFPm|XWqt^`Z2BYm$liqze!_EDKZb{4pVw9Vs zi-_tlhbuJB=ZTi!h3H3Y6+k7PYs1JDAgx7+^ufOyr<%_E=r&exC%v{Ix9g{(gF!biaj!lNY&C|b%61-h+$xM7 zR9(@CZhHz-Xgok%6#e9FD?O0iv+iJtk6f;WciUjlu&MS~pw15SEEIx0Ictbbc8CF* z{eLy~QgYwnxUs?f@k-8ONtmZw=ACoyDEEYO!02a<^JgZaB?h)Qg}<*p4cs}gbuZ;H zsz0r08Unh9nW~M0PzVD@8;u1}V@0=I(i>`YSRPabh4HMwFvMf7sgfo;zTbW%+C@Kr zP2Tok2Sg|R=ltI(D`)5-dis=Y5jLp#L=c>R(+G-98f*o@d9Q?`^Z@QVUCz>^ShB`4Eaq8VC7;Oy#dsKHkx|*NZCw)o0qsBcc>vZPXnXX7nI^h{vPAex}xN~YnlwHNbIo~$tK6wf1Bu+PBsLUH3(jKuC})w7jH z(rfYuJi>OXqg&fYQM9HV?1>6>&9pJO8lcGce5w(#w;HbRc!VuXIt2G*$p-g)VYE9y68c$1Y`_K{wu8(!x``NmWg}m0ySjAEnI!t;~u>2`npf^=DSg-x`pF4=j1J~sMNqTO<`44j?CGZz@8UduoaVLTAf z-EpSvb7O0~f3yk*eeSAM!!?vKjm!aP8;ObcTn?V#}IrAHBt}khR57G6Bv2> z3cgrX#Cq6kGi)iRlRY+Ygm4}8(Ze5(JVfo`k%bvmS{%ybEPyo2$Lb@9#29OA==YOp zl_RpE3ufGJDa(VXSiMp2>z8pXp8L@q_g0Xpl77RuR1)U7#vhp`7aC9lT}OSNrQT~z zq7`bXMf6o&wwBdmIfTwD(VypQxWj`>|0!ffO&Bh@9s4#PjHk#}qB_2(5|T%ijS3zz z^kki5ZLLLUsms{aKU+x1-y2pf_ApAQb6J_U%TZk(Z2FNg2!P;DYaHXxzFJI*o>>;X zHXY8l`XJ@NQ+5BKDHT7OLF-{e#1LwGsi6^xc z`{wS}O&}6n>G;t*nL|B_$QWdzt7hLU?oH*$(ME!U{zl3`VOz=je1s2N`M4P*-*{vm zP$ev@J;bfR+lcZS8>Nzt$OgJi^0h7-^MN$-UFe;IT~Whw<>h$(V28oUgpr%b&^{eht9k;d;0GKq_O3-+{Lv;)2FaZHq{OK&hsszXQbr%I3-gb zhhAk4NPX%jj9~EbR)+z(9%;jhyAJLh%V~QU^ZN5p(PYyR!^U9WeUaLgO{gJ-+gR2a z+>UV467>E0jj3s*a|NxG`5cZG7)arx2of#!NVHME99Q=8+ga~{SA8ZbXIYhuwflE~ zjwT;Hj>?ALdviw4Vev(>@5`Sn`^aR@LYMJtZ*&HfBvpH*Fb_d=3c5v=jNz=_-4sVJ znS97VIgCf4p11d3P>WUXAr7vt+D%n-eH^G6JSWPk)h*MPF&nzV2~?4$y_FtkV-0;m zUXMoEG2Yu-9^Lck2NB)0aom=N0`1z}T?Sb&i&MC_P>Dv#lpl8by%a)fZ3DIFUI3!v z_Ixl>gEX*q-H6qzvSrUn9_P4gJOH9WVMr}#9}3s`y)v8@{yD-FEY&h>E5~mpYpJcG z2>$F+@ydqaVxIvG|_SUCiRFYew%g{SP*nW(+z?^{Wk0BQnXk*M77z*-VE(&{}sE`? zS1#$9JZF?Vz0rLTW;pi6bTE56z!j~vd8Kw0axR-K-XID2B2SyK{1sYmOY3gBC zbKX@gMcB*euW_4H1wIy_jR}F5!!_LppL`COG2a)KqPcc@M2cPK$L2OiE5}|b_J6r1 zCfoQf-_G8w_Y#8M^0-M*HRU6sZ_sE@26=1vAX7WHe)HRR`!U4$ZE-8h)lMus}mFINv1 z>R#MSF+yR8woIN4-H5#)W?M7A@JV}(c&+I4D>^sL>v03AFd5p7s~)d$!Zr;hi(27L z$YlxYl>mg`Z6sywbme0MISvE6Wapz5R+%qQ#+8k?#vg7o-#@yrAu;I4vuBW=dW9Gj z$l_q^)}o0*Hl=i7-94lhRc)#cqfw-W=o*N~_ z2O!*lRd)hmZbr@Mv^o9=1N>C-LJuZqACD2k#-|Rv{21dAwKspvE0`IF_pB0% zuGQ&B+cJ7sRZppc4kYyPvtb|#Bu1*i4oCv8#>@G>IoDzqnGG~&C&w@H4tg~14Ri@1 z7&`2eMG$WJufm`->KilySE z1`X5==e-JysXORG3F+DybkyPE>*9MBtk?s!OnYo0f!xjzNcUMoVGKU+yd=dTF9TCLu6bOIrGnuHAR zUs_iIXFL1Y7htj70)2s9Xyr}E+ODDlVDf~@$ZKD#I;=^?_C0T2f9m{KUCf4mu27jd z{(Mme@)Z!F6Lp36^|S5jx&6O+yRHwEJ?>jubiznt0=qImGEHJtA=TXaM>Bz}WMwAB z{i%Wdv{D!KvKwKaq%1EbqcGOxP&r9{;oAF1^9Nvaj~CCXLywdu++om?zD?4Xw6QCR z6k(EAcpLX3xJ`V(hg)(zxjCuKyck>92OGVDA65AHnaU||ym!g4MwtW;{wODlCD-CI z7vXotNQr@8#o{B@*eV0A%6+$bs^R=1-Bnz9k3_mLDaNW3=$akDS;r&~yEFNivF!YN z146I5tDi>;l2gVgryS~9>!`g8&}O_j{3^)j%JAD(6@(VY4AM$c@KIT$kNnx?&qi(; zgI#>8l(ETf1DqxHY_%z**PEm)FBC!JkMvZx<;P%z!|Ee2=r2UzB$Ni1 z{=P=6#h@fx9MO)E)js2!Xjj9tfsbq$r3ZwiV9m{}&bsIy=SLQ!d7HL%%8sBf9~ZwQ zUsy4krBZHU$a`p{A6ZG;u(^z!JGe(b^q{!2J^0215WRBX~yHbor zWBJT4xf34N?1IcX17Ap@746FlUKj~(-qpJO97XC)gISpzxS}V*s5DQNyk5W3V1MA1 z{&+~b_#)-41G5Pxd#UG9J9!Ha#I4@ZDfN4g8gpT5oGsvs?it!&7Ka_@6MXZ{Q3^My z)3%j8{Eh4__0b5k>rv7|{43_6*WNVFA~HUhmGG36}%J8O}u zQo}k&nMgyms+WzyzBZqVf!=Myp@YZX7~>EFRtMGUNd*fr#9fgL+rhC6VwaycuWRB6yE@#Qsbq~%- zexq{#REgA)K^wee5H{t-?dj$h!;oO;F|#%L)5}C@NdXb9^V9np3I^NKA~o>TsV)pX zaQtzN`EZ-XvphyXWrc}%Fz3|r;3?;UrXRq#zE*X2Lyh~W&CDBRx4F}H{K#7_(cc~T zTcgB2%MFwtiw(1RqqSr!%Y$TkmE+BPEKbIHqBDCYzOoLF4vJ>J&fKuAd%CcF6f3y& zTo$3FcyygPX)i9Lj=eBOpg8FuER^gLF74?JJ4|XVeRAvuRBFj?oWW>cm3y$n8AV}@ zh-Nri9uE-v;>|DHHncRgK|E%@A?YVOW94MLxk75d+aoe|TmMph{~IscE5hISq$GBw zEXJKE@K6kiVdaEJuV@Ca=~2-Bt&Lr-Eh2UBn?&Kdu3Vhz_LLmvMOz$KAnu5)lB$hm z00KoHT2u^%e)21UpDx%Lp6rF?ELV)X^MPF&9EC?)seH|H@I7x}kORBw^&sPqc*YO; znv2LAl@QLwuUN6K4JvZ1!!4fDP3f;fz`-)MR~IB@-+TsTB$+75hCOY2#3e{G$sKv? zc!VZeBXIHSa;;jM>%i%ys_|0)JJI}VwT*u5;7=y+C-1)ejKA2}{t|D4LS7SV)pYs* zU7r+F02()A%FPZa*H=!b_JGJ#O{=$`erun&m>3IvGlO#dq+*`ZuL=x~#M+mH7U!5e zAc+iRssh8IxVxnhN6Zu>-1E_QvlX6pHmc}iW%jMw&;P1WstEK^@uWgwIy?;GCVP*8 zdE)sWBg7v%eYF)|`r0L4m9mq0yY9|yp!YstgWVOEsn;_;>}mVZpciB4!HM@qbuA8S z8?1tgCXA+WZ3ipRB=27T2PJc`L?|R!(Yw}wPKn-QT-(Ms_YCc8ZjO{qJN{vhD(~i; z^D!Q$sP zIcrIqb|5~%Wk&})@bF@vuVh%pL6Fq`0I3GTX!9^9g0!(M175fxsU*Ep5p163mkVepY-sxoL3gVPVtnNQ02g+DG3fzD2ZryR!5A!|cujq|n<8nj!jbbQPeayN{ zIpWfMcfJ$<@XM|k(NrDl0I6i_O=`!Y@!OTQH5gWEI&@j!q!+YcO|pZ>0t@gvv#+9O znz@Hp#TcCms=rbG3~8F@8{n2i1%(-Ry81u)qHokiue3Jg;CaREs8QSV5lCigrKY;& znfQKLhV~VD>_a2o0}I694`32YU-0ddgi5%JmFPs(cqqwVO5cw zLQbgT!@NbYeoRxekvs^7rp~T^1Fz>tD&S{P3i1^_BC^Hpz!d>{bUS8Dey7f9eHt{w zTdP!jdz*Bk&;6V6mr%p3OyoW=rb6GnZM1ShNfu1IP-n@%2jqYk9u<)RiemI)VSYB~ zisNPkRGb$Iy8MBDBc^Rr;Yl)Uau7*)zq22PSOfBC(r|EqHBWDCjFRw6R?F9?m#4tM zkGtv~Pt|2C`*PYD8!r7Q2?SsMKkU6_R8?);HYx~$AdQ4H64KqNfOJWB2}qZKw6uiM zUD6%0=vuHSB_$TfqCvWA(eO>Y)%$t(c=tEP{=J_uCXDriv6%C^&g+chIL>o+x0Ojp zX*l!>9`7sNl;tht0W>*Ph+%Mk6zODX%5h17`+VBc{APhrt=8Cj%R!VdU16qMUE9g+ z;XI@nqcGlTBR??~ncz&Bq4c7@d_8F}k8Py`bJL}JyzP=~6UE`lsJ=(HR0k`1+l}ST zxfI2QVW0ZYHJJP0@rK7h*q111?>DRyn02PdYS`f99rXHmkHLM>8-s-J6tM%UX#@pZ zo8FwS#;x0BvjQ4q7v8dl(;kw02FHC=88?&G(gJ%~y|aYCMk#q+>pMg;c=TVMA3yj) z;1EfB@Zjm>)D8aC0Mg2f$`LcG*`|bapNV1*t;x?O{pD*&&YoVd4|=zD%Ckjnz)S?o z28XQW{GB!zdk%#X74DW|dG?i$5p&SnCjd>$tMua`Mem%fQD+`(`~?IRFZo z1RA%|ldwqKy!gTC4gnp*=oqQw!hLOCQFCDw<@f7Qdh?Z4B9xjGs}!<)wRVg3FDIot z_r~DSRTU$JRCE|_mAyFE+um!cZ&lpcy28nP-|oARy%n%(a`?GkrF5Q2<95Qs7qsPl zG+4iApL1W-hU5l*xwpu9Nq6zOd!2FtNP{|tb9;@#>U@?5>&=wIj_k*(p-RlRH zNw27>@DLyw!n`_?2z{vMl*I;XLLW0t_eR@HDFGbOUdxf;S>r_zFGgSu`P}DuH+Ns1 ztXBjwccvS=;yV84|+8td>1R;vgh|k??u#w)IPtLyCo$N^TKF-%cgQ| z6DTZ7My`LN4{{qn9HQ9kOG!4$esR2!HuwIAht`Qb2>mqcIIp%{YPyLvj@(SeZ#Gmk zjNR%&@DNF9`azTLahT)zi|+u-jwQzQL}3FyVqE$i5C6nH?S?f>bXK+gcK346=2y{* z@-hl)x`dD6a^H$Lyt6+#ql;7 z?+x{;-<*;->K|c~Z)!87jpa_jRoHOrFq;k>o|yF7Z56Xhx)E;eqt@1{B#>#+g^)z@ z=}UtfXv^dF?lY1p_#CWgeQBcgVYmSx;cG`$e!$O@B~&&E9QA*>UDr^2^3v*A{#a0N z=A*5Oof`l>?iDqv6de&PZ8YRCDRiIn5xaU(zSW3Xa4eSy^c_JoK`&eHdx;R(MHw?5 z2e-AsM^x4U+#H31xXs#lqVhJyWLEV0804t)g-==dT%jSOZo_@vCkqtDr|r}5iM&39 z&8@_NJ^xZ@dEf{#JmS)mDgC5HGD>Zl`U&QyXimqc2jXJGndP8s*^FcBmd_X`&k47bLh){XuMcPJnvLPjm6YN&)D?|u3LGWmhv2Z_h0&11Td*Wr^$0eMmv1>+UUhobx#q8Q};m@VL)~68EX|?esoX zSd=syZ;yHv)*iYA|CuMd!&T>L|55}S0lrz~MGg0!HOhhg!^5>LZJ`}ZAsJ15E97&} z9=;`?6uiCgI7dJ$Ds;K55zH0dQhdCyc5yJ$+iIhOqG8|mEu9p2q`m2VFf!CZVewmUAsB zf;+*{@RbXkex{m}FQ2$1D#b!^ljxx0z8c)p=fcsytUo*LlnoP(nT z-5)PI|J?L*2_5>dm!h(L6-fF{Nqdu6^mGTkeXALyYy5gXX60pO@Iz@rqqxot7_}-Q zFZSkd#}A`4gU(RU*MJ$2q6Loo<$8Xq?sEyN%MYFOJCj>6xEilZe7st@z1rM;%!C5A z%&hwp%S&LhJfpmuVE!z|dJRooY@RYrt9kt*y#trmm&vgiFQZaE*VGn;Eg8b!1vYI) zaw(}@p6*O~cxPt}v%z=WlZ6rDp2>XgT#6?MZSk=Y);5@DVj?O$hixjhR{!LwHR+@S zk#U`yyc84q;agGF=GzJ@5x+s)3?Zr1D2x}LqWSn%WP2KHRBH=;-a$bQS%J+PetZJe z_>v<99m)`bLb~Ol0mu=9yk~`hSYSLQ{<*FL`dl~WhfmLwt;gy>DP4$Gs-fUeRLVLvoA zC7uTwI7%GO=|?WjS0sGbdAhZ}5U=d&`o?~2a!;Gk?}xZ6_EDP2qegQHbN4?7?zl{Rlp^)I{?f<==-@cVLDDAk7zt<>728K)aKI>&=`Yr=WD z(r(eYESc=WEHK>xfd$d?c2V9SS^o2v?Gd*TT!lqBQp)U3A~UiPAv+?f{bg@&)QeQ+ z@MZ(v`hKeWB3ti{vyg6@x_*5+TMnzSFTfPqW_NJmW;;Hp?tR`9nejQ}Yhf7e-17|N z56>h}PBkpEdgB2?6;8Tlt7>suU;Evm%B1Ce@eCggTCf#`UM|T% z@Y6HIG%Ld(t`rQyj<+HuQb#Wt9p+kMVuNjS*&hcyU6lbg$&{7foe}{THR#^O?OC+T zDsdr-^VK&0v&?f?@CRRVLRDHh{Qgf8heR~dJ4gYZH)_ewC4xoaj^Q!6!3^o>7KmQg zC-qPlE{hAL+mCF^?i$So8pW>!au)E)*@Mk^Crx_}SD31bax!#DLCt`kZctk*nXFu_ zDznI7i^{dI?uWqDerGugG26?h3L0%Y_D7r{NlFSHdm#qRH=2B!3TX`enGgFFQTo#v z)GW`!pH}sB2*k7cgg5JrA7~#{$uW|mU{jfPX@ zUBF3fnP1YhUO_y|t)Y6*9gWg7jRRCQY#h(yZA&FN7$- z<^v%DK>zNTP9+tK#bV&e+($nBt@=`(Q^{WX1ad|j$#3;@+W5Q%+$7(hlXC6q?7@DxAlFL?y^gg_iX@IAess}!Zx@dbNIsNJBLpA;%iREIY-#mGh zz{|Ph98amr7ELkm3ruMVQblzxB(n4S&N)PobO(*9WPEPUwXCm0FfKB`C}#Lf5ck0F zd~(rGLX#XP<^*oHJsv#AOBUSm5b<2}I*LIWZYV=rcUfPYpljCUu_-E0eDYdjLMIVR zGGEjOg(SjQn_dfCuRGvz>goC3QkJwr8ONnyhKdeR$ZcCi-pHqOV4?YNnLENE^3gND zo5oGpR)wY%OaE>%r-fXyTiBWJt7V&&ynTcmW-gPGp_P-U z+EcDqUjn{?6<`j>NhdrrR7g5PVv!-Mc9tFg0%A1M{&-y$%5H)>@}-wNS1w&!pG>F9 zSkt@`Hv??+`~U*BpM7w>bd^SIs9rM8z-ZLA&O&!}II!L1$hM3POl-0Lh=UfsRr!WK zLz}uR8QX?<$vjGl2o6;OeRNgY;LNW+_Q&u?n;dkr^Fj5`Yarj%&^Ho#9t8CMNL=TI zs;&e+L>WlCs%pSEMXa&KF=}7wfsNUB|)3_hO`mm)K zScM1C)nZ5sxqUNJRuu51Z%mm!-zPhc2p1#Al+}>Yekvy^Hr(hn1>tDUIF9YiNq_d* zZB!E(Y10TApNWSeEwJ7XL0wvHAEdBH+5aLoBSXq3vV4OSuoTt1a3d7^BX0!P~dE1n_ae)y0wd~ z+YL_OMEbedc|pQNcFG?~A962}l)Ol_7w>_50%LInc=w5hieC`W6Motf^VCrTAuIW; zp$D77|MLYEenI*LS_yT!DRA%p>`I8TFYazw)|rt(^%0sHgX&=8Ce0MqHw~zjQh~!Q zmGw|M@?02?!-R@PGf3|1zTu-{G9K#|-K)M*!=}O-zy4|a*6s)wew0~A(v_$ z$9xjp{5n9uwR#PbPkzy1Y~a&{BmD+_=U7vRgD_o4Qn4Lwb276P zY86E+w?+&ta)&~fVp=VaM@OIO&wYfREcJ!~JBkmzTeZI}f=0a6U-|$|-KWdW8jZ$rYVp_@W9!7I%>^YA{3L2J+pp ze5TMS4(u-tmADC`EF*OvuzumV_;Iut2yltm0> zW~QNS&BoziNeg;fI$5+F#g@a)fay)WL|nfKz&92`AyLx68)!(-*XTK(n4n1%Br+Qm z4BNzgtAAf7C!nKj>azTCa>LEVzw%Zi-W=w_mh5cSV_GBe(7w@x>uJ?djgAk z){x<~t5eW+_(rVq3z9aq{r8oti!FzN+V?(8o6DVRw1^>|VPIbH&Gf|y$%!PAQEIjA zbelnf@Hvf!p^?L5ho+8GVer|Z%}Ec5Lt2Z?q^}H_RzsCe-EF(kPIyAr8*pyv;B5T{*eQSO0RxK6E;VFJ^vt+!X>DUp|v&P#)q+YVRw8QK0VDcegqN}E1P|V zyXG?^gyBIj10m`%O*9P>Ap3l8J=qq)FxX9~^_{C?Hi?GTjN=M*p?fjqq1r%!BdGs; zr`MGR8y_&q0P3uh-fr}U0ZR0(LWa0rsgqY1Q0tS5zhs*Z_eU|)o&3b#s-_L zv!36!iHu_mZ5P2KJO)#-N?z%%Z(JXXd+|9CgS^~!MmzANS}<@3*qKNyEG38pOh4l^ zT*WR2!?UHyxzbo0f4<=H{J9YQ8niZ`b$x+_p<^lCo+r_qxS|u@MPsg=?&NI17g=O{$&7oI+%P9?J~C2#RQ-8qVHonppBH=b&?~wIG)U+)1#B|+L z2uE@hrxT^Xg_Ydufwn+Fe6t11E%9YlYnFYzZ$O5@1FuHkK#NRD^U9 zJpekXeM2W;)=V}e&(`RtE9nV@a``w{MS3(?hzujJb9OPmsZu##46W@6e~!tym=Z%p zfDexlu&4}JxsPawWMT(e4W;-P9J!4pPp7>qNn(rg!(F4aM9|fCxp}MMAg)H^yEcJ|rV2Ao1#D=i0iMgnArhg-8d?S(jgo_vKNsm0Fo*^Fjz*penfMKaw3x0G8m1kQ z(3wZj&o;TKEU-QQ6vqE4!Z~*ka)`3DQ=&01_e#w$UI@l+eaz(vUp=udzhUF5eXfoh z@g>xHX3vI$cBS3vdyibA@RTNi|m$GzYUj;FIFjGvc=h952i@6DO5p0veImut8kF2umXh|sv%3KkI85<=j z*5sS)=weAVGmQ!S6ZPa`|BZOj_Z|XQ-aY@%fC0Ik|{Aid*Hu_c`v{vFD`0^|3 zW=4Ht9_Z5nuOK?MVTpRVeGtkA`byRm8^gYx*F>={(zPhU2y5khgkBS5yAiE6uE+>E z8fl+0--Rp+yU#fRte=msZ%zQsKxFs$6>@|y6R6r;o&-IMQ@RG@4Q*;HSS`UH3Q&g>Rm2ohLO*%niEgPB;_m38}XkKQY{(*{xL; z^kz}MWc^{U*M{8)bu4=@S=BY1F+rr1>d}VHY(X5oTqFgbzDC1>H{0GD0$ZD!Hx?@r zna1zoSXA;5p*`Qvu(~Z~RUVsFeDg^M8WXh1kJ_Bi9n0VLD`xW77+{@r)iNzjks7cO z@e(OkP04v`D|I07Ljd89ndCIDosid~*%TZuJ#d;Ytc<)^XTBe7Fio(jX{Vu6q(Y?@ z!lGAKl31R#4@1#xg5XzN7kZLbb9Y6nDIigGexlO-NY2#(B9grP^fIr@6-!?13wPI- ztSnJglQ)zS-bjdEH+&9rSM6H!x36!u9>g6|>1fl)3%qK3j{VCr0ONT5)c>`|4ZXZT zHI7drMTzC!4GL@P)q^9W%ZzVtZ!VCx+kx^Gb3Li~J}mnJK?FV3A(Mx^!se3>Nb@>+ zN0pAI{fS6mX!4M1zy8HmGVc);JYJaBxy)&joShM1aM!)*)mirEDWygI|ga`8t)iZ-9 zW#1vuN%ouLYh(Feh^bg009CTQ6X>=~_=Lx5CBN~u1BOyV8N*6EF-2OdLJ<4%u;}fU z0o9fh>zL($;CtR90CWqrjYHj3gB^M8`8+Ef+$+rTS+qlUSJ)O>oMNE5lY7Za00U?KAXULR?Ng@cjpvM~CG6ZyH{c z97cGhk0bboMv;5j9N+k z6z|UHfWCQN`Ai|0xA`+8bqzksGlC`{vqZoJFbqn6e9CrTncd>ocF0Su?AMoj*-2fJ zS=n{^`=6Q}=6&}Zfe77@WJwcJMadTJyA+$0Jsdzjw7N5$uQ&Xb;nWR)8XdDUc)Zr=K2y#-_PuE9Knkq zlA9Z^Q-5Hl*RZX0(_mWtJ10~$AhNo$yp4>fFR z_B?O^I!5}B)gBD+s>qBIbLVp#Z1f<7v>p6E(7^dXZ~)}_}`G745dHP z4=h>A8QY|l(frD^T+@(ej~x0pKX((=nr{tnT3%CtjW(vfv8$yZc@>d{wCFo& zq)OO7x*rty7F_k}5)V?v67L(Zv!d zO{i9+yWqX%4naqIB(Ae($iyL`c7P`?L?aZPU`x?)?^LW$GtpXTaqM7b%lNs7xpIW7 zjh;-kUpT0DQuYB%B&GzQ3$vw@f>EMnPk74K^6Ns=q@MJhf>#_oFMpsD^|%hYCJZt@ z8h*sLmD(EBO1h=eCbL7)Hd!*~0seL~0>dip`G}ce`7_(QQ-ZX&Q`S#IsS&;9moyTI}_jM}GN)q$J+sAjL=voW;>BPuWlgYrc7nmFP7DRiqor0&W6qCRc~eQrU80 zfj5Y0!WioqwuO$!_<|WYb?JgpJcr(&QyyX7_;;R#^BGjKlbDqu*0Ly=)b#=Yz~qqN zYvP-xC%kga_R?F?dBiY>B>^0%YWNu>~R4jz#De)t~?&s-FK;B zOXK*0M3*d1#9{ls>FQAaHJ%@rEagAt>ry}oY9SH+|F*n#-6PFfe6Ib2d=?i!*~xs4 z2zEs1yWB$FEsE&rS$ZGsoX)}P=e@hrrdV_r=|GV9Yf~(gLC5jPd-9lHRw0ot`SMvy zkLL+M?72Ur2n`qPTnKCY$X&+s}8F z2rd1s=;?L?fI6_2O9Th4*t*n9wgqdB&jN zv4hK!LLIjA9W$Xxzn14@C)%^zHjsgFwlWG68D}wSr)AM9rvQ8~*V;-cf>^F#pyCE19 zX0e9ePRI_{=XYrMmVE=XA91W~-e;0*6PUik&E@^=JL^vR50Y2-LF~bZb)%p)lXv8h ztx6V^b98)_1jc!D!+l{A4B_tWix*V`pgET&-!|p>|($YovGZ zA~j05h;p4$l1X7I;Cr%aAhes%=nZ^$J&L7FF@;-`%wT;z(v4%9;}T{P8my}TOySDf z%-oE6hnVxa#&}LaBS(@WEaWP@C@6t=F!9}KD;9A-Sxb!>Q8Cipw$Lg1Dp?iI$12SF z&5HmzJ*)c4@qk5l1fiPr&;CMRXrg32MGZ|3d;a)#Ql{D+Sz~6wCjLh6s57c3$7!WE zi{X(Fs57?PrDbY;`^2}uj(tV%6t^lWxu*b&k769D0>zI|TtoL@O5a zQyuMklDUHq&Duhi_hDm_{m4>&wU+|8F4vL2b3&rCMfjRDbQ&|$=_Up-`RlpJEkxe% z)qdg$i>}-LL^FC_;JUWOzNg!gI@@ie1-eVrM$aOVVP8q8p+~=?J_YNCW#{cvVDxaX zyoH5Ld<%y}E*TBhTb4h{9$XLRqV)@sq! z`H|Q3>sviWts~|H(=W*ZX6H>&`2v}re&(XO-kayEdM%_TBu0p`_X>O3L#RxIPLVVh z=DuDq=d*V|F3&QGwZu7}oBpI4%eNc$us`OC-72OB)ZpTSC`(?^9nUWx_1ed@e&scs zgXdssWV6L(_(4H5cDYoB2tJQ4g<4g#ZaF|)9&~GMmXxyc5kyjaD}gb@~y-YX!sV zo-Y+adZ+ee8VQUEGt3J5=z(aHU!@2&P;FvXo1bb~5dVg48Gm5{j?c2tgPeXPiwL+f z2#DLEKP%*V&}}lBJ_=Li+fI#^7-bc{=)xCR=9}{d-00M>*B6jwy2KN&WQB;;kK>(K z`Gcf%m&@Ik8*PEYq3zlf@c_#;EisQR-JpYI9Np#9;P%41!{`{U>HmUdp<_B`p3|%qFS5DwCm2WK#7n~ zuVAtLc}~Npd$Y~ZYpik2qGy4L3FgfZB3N(vFZ8UoF}6S@gCOcw2)b*ftK@n&R3v$hp20Z?9&F z(6@P}K<2kWOGr#1KJN#92@Lm2%QT`FP>F_&ufaU?095N0{*lmI8KYoA8}Pl-;>2>n zq68)}o-`^YXpySHV- z_q{b}z3G{Li4BqGkYo(f4r0jbML)~ceG}glbmYok8%j|Cwee+N8e)<+lz4^L6Gc+N zsPEV&nZVK}a>vptZb^9CM#T0dFqLE~yB@$h13t*X<<3Y0qROr>;Up2I!4Zj`Ypa$} zT8{=uyd6LS77Ohb<@6;1-6TKS)X;N`ii^h#_Yb=d;uMpsGwgG{7hX~NA-Hyaob=S> z3&Ssf?@Sjm&U)7Oq6W}d=m^*9>x{9AUd+qY%1nL?dgr6??Pe|AC9&kd*i~aQJ{B#} zyOPCgj2|&+2V|l25XT27<-QUU_}IbR5+#TZ4&T}#HyP+zhiRj)?UL}?a$4z|pfN4F zV=jFOx;P<+;*rH`Nt3z8LqxudV(AyM4QE;H#>5)>I#rhGRfb7I0ayb->9aPw8>0mf zpPxuPQxG^IP?zHG{>;9DXp;WB5_$FhjspV4ny}m(kuqGM&Y%r3;;}wU?u(;WZhC!0 z$o#4`i#*xBwH1-A+|&s>{>UXBh2W+8+{t@OvLM1i^U9%9iw~fF=6--tR6lvb{qgHJ zZ9N6j9UIpIpN?c5bSk7Xp)Kc7G!?;h((WE6B~>6hbBFC zh+!%Z%g=lg$kW3FUe4Dl6s~}@;OlNCd$5lQ6{pJtsY6=xJ)7!|z)VaW-Wa-ccI52x z;JVam7Q-1~szV?66rqJ{wSbs(_I3(t{eB6yV$H1|}+h=3j>vXSQC*bN?iq)Li>y*J{(LoD9K?IjrI$<+Dg`0yNUT zCL5)dr4_TI7MERcUmq``eg<%jKB3U>1Jv}NtQCXgJ+du;dQD^NA~M^vj0MztM5dp~ z>PyU`L^NIB%yV+-I2i%uG&3DgP7~Z$%-J^9Kq$>vJ8|UEy4S9tK^QMK|H!p@HI?aU z;zj4mE!R@~Bb2zVcN|^VX@GUUlUNGCFHotlq*EBVrB9;{@lxd>DHlsdwhfrG+-(V- zfYnH8r}DhnFA_Y892>KXd%Q0Zyy#mcUTw5s57U;=(+bea!8@wl%x&srp=EXx4Qn(YzV2$}G`6naImXz*_05(ivcW zgU(mCqLUNM5RcGlN3WD_;Y~wjkMiiiW~$n$tInn{$y_gabD}n|NnCH?c|6X6&ggS~ z+tG%$wy1Ra(igs5YGAjQRZ4o*Z&Pqos0tvu*lpwX4L46}<@KmFClbw2V=COWd2_Ct zPM>e1$cxp?kLD{sALm=puX*a%d2da~`X}WuVb&g|J~7_!eY5_b`$h>HQfrZ+c2!s& zjJu2h;Hx_VtvbZ4dM$vk6nIsH&NVPv$3pO}(<@T>Mib;Onpk*Uy^+;0gm*F2z1`Jb zb$MjG-jM-Z_CdtMBBcxWL}B;*6w>${RK2)MTRe_R@RXUe#fnJrc6RB=NE!AG0Z<;!&X{mQw$vd$^<)Dx!=2OZ5cYfM6+6@J$tE9ZEeO1Xt@bU!8gl5JnnHxm? zCT=F!@gGmRh2ce%K1C-umfvi~+!4+6Ds-^W-@rjFp?G1^R~k?g{GWW#3#4ZX`6}i| z>ay)r?Y&)k52bT7F0Y-;7FNZj+}>TAmm(^3EcE%{tGUJXb*E4VPfnV)Uf6fc{R z*S%TmU0L|2@?RPE_*M5q;_uQK87f5lf>%Y=9wjsjK<(d!0D1F?`W{6H$S^(EvV3~= z+&!Q&PXJU{`t)$6Uv(|kHaMR>!ImdhWJUIYiS=?pmiiquIrYp*v9nOUvD$nbe0yEk zHos-Sr>NeSJEs;+n;%L1UR}a>$GBGG zUpe-80`oRr^TWx;X4_8ZLapO`&#rTN(+OF<%Tg@ro%7SYE$0xh`BS_lgkq)MoSm6@ zKKSf6xJ`RkJ?Nec*7xushG5MU7m5sR6TNkUH#jZRA-h;aZiEiMCvj*|GSFT`w43lw zW(b|9;5WU_%A{Iv-qRupEk28VWJH`C-uNm%d_!<+1Xb;;*C)hYV_oWQoB^D z3wOuP%ncxiwkXH|8d_i3{b(3D6B1ZH9vdgqaF9Qh8W^N7_qh#R+02d71O}_v@@9Ej z>$pznSQe#mlYn*d7j8|ixZ zN#*6Gy1-6NwI@6!oQjJie>#Rqh7tt_fM-xN2qIEHi3f(Tw4_W|jSPI|vr_mvezM`V z6kdedFuYsjW|~8)Oi3KXAU>1hQw;TMY2@p-;IB#+r~-hpr$cST_C^)Tn14bh#ov%A zT>MZxeSHLE^mP)qxqUrZbhbIIX|u0?2Wml#)pT<|G{wqo%ucvrX)$zHizSs$uHIwx z=Z5El#2R9&eI5ceCx;(Z3T<*U0>D;ZMB822eRo z&&2169T$FlQClm-w}2g>pKT(6N$MOEW7uo8gq-arvrBc`yo0~tg78l^#zZpOSKNim zKtlD)y_j0HTinNOw$qoNZIc5|^;uXikoiw-zgIQ#)z+r1GKv~~Tn2IGK4ZNhpTH;#{NZQYGFtxTvmfOP8Oa=qd@fjCu#K&AZ|+1usacyOx64JUW|gI}k468?&?C zu6OFe82xhHlXA}*`_z2=y5S6w&ax!2I`&{!9`dq^J6KnT)g*PJZl`&jEmADipbBGb z@+FDr+5S_z;FF*vQb_M(E&WZ!?no*}Y!Av0RLrAm764n_leSkxFIezU32VkL%6g`# zjgK31>_jxb{Cy&;&+PVesil?(%?=&N7RVUbgE7Smhi0MEdo2#5&ndU&l_OqBlbS4z z+VxkD*5xXi%WY#I5ymzv76fd|E9}Bm)@Bmas))$dF7Uw6E1VJA#>)2ht(Gr%Z zK7EjXX?-88#ACz5emSpy@IWMQKid3NQ=kA7@V+tklT90kbeYv1nyGR9;(`J@4j-p8!Ai7Uf^+E*QXBk{4a*rxXUD2z5dO|j1LDL{sl zDH(Vc)z5iO=J|_1$Y%Dtxr#Ly2iLJ{{ASiS;K9R17A&xo6Kp7;+Fi}}uhq(>D_11N zWWC&MIy$0-8tS>pdeAEo7&{Jlv7iQyY;u_5^mP0oJ*fjd zM?aB;fa7;Z&`jC4Wwg%w#4?nHxPbdEoFMI2yweo`mq6BU*}?R~zeQ?hJd&XvykN7e z8#m~|$ySkP6!egV9w}L)w2f<;9jR0%X4!k}ygUdx_}wITknYi5$>$7n`KsVzQDt{6 zK!0P%3kX{PH7u9@BZq_0Z?i&s4$xuX&Aw6xUe(E^ht$z$GpRf- zavzZ6wxf*n@V7@BTj4y;X;ZdY6jv&W8Ju%-gL=LOSq{=WS?C?LutVGp)B;FpB4-9G9{p<7_mT`yg180g5mCF3eAa_&y z9b7jY>S^V)vA%lySeCYtKZ;VNX_Uo{OK|;%c3q zgX%5W*)Yi=N`F!A|;vX>B$9u<+PMXiVsBnV0C-8eNSZ952rqs z%200H;iPXhAc#Op{tNi)!~Q2!SeZW82vP&2fvng)ak1}3B=-Pez+ILa*_iGH--e3D z&8f*JvZ`hIUPyfyv*iQHK>p&tv)x@JJoe_K*}YE{o=fT!hgO(;UA&}VK9%GI-v@N$ z-W;ym;|3j*@g(eJJ3S;7JihtigknF5NH=U3duA)`Gg2*{7`po~JK+>?E~#GMr1p5U zGAV>1>oWJ&WOPoi(MvqOKCGTjnuj}X-LW=nY)tx3D(L-R(*jjr^~9rosow_P_}9Sy zO>Y4MhgtP5Eq%D`JWV>kAP!e|Jae=jE&I48Jfn!dl>~@S;TG_IdlR&w9hoSY59r!I~b*12dM zlg(--`^fX}K*^~r!sQz@H7p92fyB?YnF=g;?J<9xqVi@w=VEzC6AK^&VR`*6e;W>{ zf2aP%3IAz~pMR?|3v2->qFBE_D-w+b@!=?$$IqCOMDktN8;`XTPII~9J8fRMA*L4G zv0PtAh^1X{s1oX31r+x2aBZmvK)0lG4pkT6HdqvQ`rh%~@1z2EnE z^9Rgy`kf16u>IA1zI%mRR+2ATn=DfL0Qci1)OB;5+nm27qm~fIHD&`VtQsW{YL#aT?|30YsKAO7JpY)eIWE7;(%y z)50w0CrJAZ>^T1c_b--+M8^Kq}lU{R#FR$*I8n`^--{!IgY!~*OPwCrm0}yxgoV% zTs+%+TD0Y$kXDSjG&3{BrI6|f83f+5_L#KM?{d~Ut^$A@i!()p5st69$Vz!-GFdE5WL+6gIY+(zsRZO!Gkd z3>4@sFCH526f?2v1;RrDV3`87tJ?Ozck1i;Z}Brs>2D2pkVkWGZ4PU)Z0%ghdb%J5 z;>agtJ6&fmduwbrY;|_kIGi>GWjCAVnbhEwwd?%>Zv(nTRI6}fqh}%NM~OSXkmT=; z-RMRNd~xJRIUyf)Ju#>4OwevXC8VTFq1o;(mdaX0f|j8?HoofALsOtJDO~$H+iM-@ zyP^vBfkc)krGU)K(lTw)*#ba6vS9{Tsmlv9bjnY1`{G$2JL`XV6J^6&s_A1hAvV&M z(6p8*H0Ydn1mg1dt9XJndEg!0;rxi;wLKB%+j!5L;G}`gv=ewCmTt01HvmhR*8x*4 z6-Ca(U6JLv9^lGY0QE49%TAT;pUBbtCvuej$6=K>-kLC~fsQX{?j!jlFNvG5K+5#b z!#vHu*<@BNps4^5q~>1RslPydnthAX>}Shjq3aqoD4m^E&5J#ut?ovuwT#*S1tPuc zQUo$~LScVY?MXKp^k_Hc{|Tine_FLi^rCLXTw@Zghtc+-tOMXd`ICkF|M~%gRBifP+ zETR&_RFU~EWufx))PotSEE7X&)@X6uyV5a?F;I%?kKpC$@9ggTe`I$&2wpNq4FQ?% za{_2f=;^8QXS#Z0+*Yt2Jj>Uu*Lna_t-Jsn8MFyrmKtdMqYO|#ZNJ;OK82d2+l_WB zU1kyWWdrzE+B?z`)0f6qIfehxk!z=saQs8evVte77~4Q{o?QI1Rf`>xVKfsoVsOj; zV<pCpRPc{Km-~lE`gLd( zsMrQl`OP;U8699w7y`Iro?MbBvTQ5%;JHT_7OVJG`u?{1Fl1=XH!PC@hm;M_oqN^l z08QZuB@(^#AI~=E_4B9w0RX@luzn7yEqzPKeQUVQ?It zJU-T!=Ydo~=>=o2*(N|TGw-X`9ZsN;FPD*X zxFQd73@@O>Zm6@4LA#{z+U9^wsuH_Owt0_ir#>BRA2}ZeUzNc3rX7YVyTZDmG05l^ z9#ihyKeBnZ;I%GmtX#HL?+$VRekr@Hc_t8&MSP|4_*t|ek?Rhe|P!M7a`x}QiuryYBaxXoQC$djZ^+> zLsQ2C~p2frPFIp#iFi~!0e>|DxLh2N>&wv z8R<_PNf-bA>zN+Y$FT=jCfL+K@mrR&w2!ql^7d7gtcYp<30DYrsP@g7O%m*IiPdtrC_wPT;cInZ=&I2TXMP_4iaJ5=pYYQ8P{tCw z(CO6|$Yz6CltKxYsTfUhNnr4wB$Amnn)%lq)dK2};H9wpub4ZDJvnypd5q?SKXCpQ zGXdK#j<~XM2T=dTuR4O<5OtD}&Gwv;{C6f)V#=C{p`Ig(+@CHMEsI#Jt4JiY*XpXJY{Yr7pP zDIdP^&-?)y%JYXbYQOHzFjglyHYdld!Dr3B*5*{S)^gAH1emk$k*FRU0{;Cl;)vfQ z?j@>v9FkU`{Twd*#P}{qaCJRE;AONkLp8+`?q6*}Y?^rW>D}?!p{GYxQ;PX_|L!E| ze=mPh+4qomH9PzQQp!J;@=rGO_qTWeD$rKi{wmq|J^SBB`UNrmy{CuN;wF6v^1u(| zkH!Aoo_{{je7G0PfWi#?p#Cv{|M?X((UbsNNLuyZ8~M8~|6P&42KMJD|JO$TTE{;h z{%a%uwUPgN*1xgp*N6WPu?7?2+-BPCl->OB z^su{z4KGgIp1$2n!PJHydbT@TuME86i9n-Hfx-2MNhmD=s|&q9=?hBe?tOtgU2O$` zSJr>|{uDN#^(>en)TH{M8U^dEcI){NiW0+9=_m$u86LaiBH824s4eI~5l#C!3w2FaCa?IXo2DoDDF_)f=h7<#jO-6#odDzcMI+i+&#!Yy}!BN z{l0JR{O8OhnM@|{d(Q5=yXWk)&#wAz*6mj-v&z<~IjANL-=r>eh|SzeC)q4NvDEoL zOZe+ceRCmDkn&+rq8iX0~>^^X}L%NzpYSdSI> z`?j0T=V|9qtP*(~8~M7i_UEUPdmIY;NgB85PtD*1S$1(q^C5NJdE(dzc3{(GJj;uh4tY!NL}5uQ@iBZi%~C`ic7U@9~LG#8TOPRifQTrU*Kh zz5Qbm$C!Pc_qH;usHA4rBu!pkHn?^AXt(Jy4j9V2I6%@ z7ZJM%A_7RhlkEabU}x3K2wDcz8{QV*>pQ5;?k6jzlQ%~* zpNNIZou=X3Cg-oo1lEPF&Wb?kq+G7tzc7})u{9A$;LpqIXL@d{VxND_ztJvI>=8o+ z;^z2X7a({w7-q_TIP}jvIDL&uZY@WEu}YlcINSyY=wKP>5IVH?)#yg|r*@Kp`@b1x z;-A#K-wf~4Ks-*wXG-0)6Codl`exFKQZBByr9EFfKU`GUEafFCemq#;-%l^$z{#0g zIPZ$Wnx=GY%PG$;W!xh=aK?FRs zTRvww`6k4hz9dH^iEL`>NB383D}}a;d|V^G{r%$z(H>gyRrAOl(-JW9H;(s2In@>SQNk2WG5T6u|1tcwEP@VfYNlflam<2(jZPYMvXuRzOfWjPgNTA#%R_g zcUxgm`Kv7>g{kq@nOCd-_IJpAO1w>1Ql^J!J5ix@F(MfohJ(AGVPQ?4e*W{pMq5aOeotRm&L5m3F1Ctj+SzH;{qv4eHp+B_Y zBEkh*ioJmFgSRBAEzb?-JQsYBhdav>(@bYLXB4R2IZ?00{Z~*;17WO$YL?Gv+8>>X zTVIzo&q{3l_I2hu7bpi0TYIx%Emdf|$=*o>s<7XU?H< zT)v0g#^fZ{WZ+u&NBOIY@{ByWsNYyXLEyU!rvc^IPnRL|n4pB`+fCrrV|pC|RXDOg zXNp-orN&y4g`7h_;OF1@+;Zao@nF_bebM8Pcjwe{_i<@m>ix|`a=a?}fW4Kh?@upHBcM88&c1QnqzSf>blw=M94*^$Og{o6)wT-u`QM6$>3!DSN+_V)I!^P~7u zJ3dF-){7sAvM}lW;T>B5o;@05lC#G(hh|hubG3a8dbKMs+*1xTa7nXAF@9xHINba| z7UY1o)P5=&fOk<+-pIJM675=YC3b8%vbXyyPMvr#$JAYJc`e(h&{fe;fccNr$vUF; z!zZT}Zz(Ds+62q|8Ouht%;`}19nN^%#+;+sCC=wa4%YezBYWp|S3v50P=)Wf98zMN zf)l$w%i>Eb-yHV^Ao7x3WnCQfc)WN-AIi2#v$(nUlkOmoxNymq^hGy+8Y|woK&j=1pc_?Eascf_m|UBykkai@1~D%{vM~#?WY5lpfp9>NK%o` zrLjX{Tqyr|ufz=e@lVcJ;s%JXsNHbA^`c3MCW#+StbW{jI8~j_ry2PuYMjrSVu8( zv9bbO`r2wwazu60y83*pL0$IvD(^YMC2q$k@+PCA_M}DPdCB6s(+2?o1vo@TT)@}+ z+?vH6j2F7e@5Muo5`vb3xxEGYRdR;x8$6Q{xp>sN(V@RCdS z%QEGZch>jcz;;`$`9{B*&=%ol+aj%78N>*qNEO+no9bQd!a7qk#(6<+n#13dPqLsE zdvrQ;^m|cUfHohSTB!3P%M3;wx60f~c1&-8Xeq02j@DW)A~Y!7cbii;>uyO#gx z`ms}OISZtbUN}7rS(~7%UL28{-hPpS2S^ZLuU2+{*Ire3*=hfxwd@6F|6}tb<7mWY z&;^(_S}rpEPMuP+XzeH3G3H*x;!%ysWBUI5z?hG??D*cvL|K`~QVJ>XB1F!yz(|sO zf7-jlr?{?ReVx-}@`7&XrkWZSie}p?Ug!YX)0_Gff?_PNq<$uRq zg%LUjctJfFc`CcudoN|Mz@~6dIOG=KYqxUR$zw8!U1z@-8!sbSX+M!)?no~4nkKr@ zYf!A$VGL&L>{3XR4hy3!Z<(yc(0!g20sJB7fhqp0CkZ`5!v?A`^s4aZ>Ajbx?o=i| z7AQXrb=@_%B51U~gjkW{%*-a=%MybcekR4?Q zmIky4HT4hxg~2c%xt(9WzO6gJ&C1~)!xjWOEafH-^nv-;7^G`&oZw68yuO+v)8g@K zHG{anyXCKJ)9{3@@12IqY8l*X$z5X?_icNDwFlEf1`3T9&anu_34yQdir`yf{?yX- zauL>y57%Cnu7J9W@7uW{ESI_;8+M0H`^#HX-#2ZM%G^5P$qA!3?sh035G+s!_-stA z%Y*Yy`uEneC~=hIp*~WK$6Vvp?$e1;2(+6;LVST%*88|=%&0Y9Y@#l19=L?h-D*~M zG&hu=0IM+U13yiz_`+qzyc*}v)&NTaXI02g9bH!*U3D7MXt8(_{_e|jDh$hbM_idadVEK=QI88h_!K5nHQO$>I43DxeEvpws*H|}{ zY8f%pH(hPDG8w&tBuCD6E%sdjNaERt0SBkW<@VEsBl^=Fbk_F0E3xk6qY7WyPEgbK zT;`AJb$>P6oSvS!6O7a?H_7lY$lvVp@EWdCsGMSVDF_OLlt7(sj1O~WPEH-txG9S` zxABKZx`>Q=)*~nd9CRkvwuwNW@eS4)4J=WWL^9=-I!LK~=MfAE4J%lx@}PJ#&_kE= zNun5uw4`YWsh3gDL>v4D zq*Vu(9{d_|jt0+AQb*F&W_6ftGj3Vje7mZTWRk>(IW;AuxemZtVt9ZXTLnF^6pE< z6;5q(HeqNTQGJPv{jN zQ7mwBQ#AWnqN@e9QepwG)cTENYo%po?M0%?h#h5o3cn7P*!6DkS1VJmiF|X?3g2Ti zyjl}<4Rrk1z?S?V-DJz){J3kHV-E+<dp735sL~?qZB=?OXgH-q%|+OwI=I%JShv=8ER$l|MV# zL}N7#9_{PB>aT9%v(@So58FV);%}c6;I?Rs;?m=9qo?s&uZ3;7n0 ztejtu*ZU$%E%MG~R31xJbaoEyWqE(J>b|G6x8|D2HNtVH{R?3VN`s3mgWJ}%gIk`#gsT8yp$qO-NEqA90KGVN5ID+<|C zo?w~UuDb!gJ!bhHm$o zzGDV84>-NbN&(XN^vR-xpF@87+n&a(nrL$MW9rA#@kPJ$_wkLu9;KA8P-sj zN=`UyqBCe@(J)f8@CDFAdgGwDRY_jY-9Hx6VRAKPZWpLrLZf^lp?3@dpSAN7a0}t| zc+O>}4UipVpQ|4!y^P?W=$>mo9~$@Q0yYo5Dt?~#v;KA3M88qM*;J61>Fw*>s6MT6r5(Ijawhsvq3jR-Ep49=jU1_{GdGek|l?rx6HZpHvKxdoxOo45~SML4U)8 z{kZpF=3g^%vd#FtJ=n>Y>me@@(q}9p9AqeL6%`jO^S;WVx}KY$a|oigaQ9PEcLH_m z&0tvtf>?=etAyStwZ+5QUT`hWo=(8`Bc9{Fn-_ls=Nl18ok-rmP#=f#ITOGb@!=NP zafO(2ZjOt$;GTkyoZ+VUHy;(P=@>uudGpu&2?z!p`0uRn7DP-C3cE*~S*F6ogwL|v zDBV$x70w@moW{3{pPaEY`WLTY-+p@i>ifVI?v{gcErz`v{3GeHIy7s;>w1HRkz~UC zd5B2*IUx>k9ZrQ{Pqaq?N@isrS{{4s)^xs;8;tN9&yiWhMz)_KDLj4XhTy+&0`!W> zLUuM+u(1#{wekUc!PvmK9k7*qwYuacNzhp zY{}VxNG_7>((^X=Rc>A@GcO=z3rs#sXNk|-#(SOp4IgsI((`VSpEBCqEAm1+)va6% zLf5F=l6=N|RI4p1_f86oQ19!Pau~rIrE3>;Lz9V!Tiz;bp&<;$1j1mSGw6lzs7nS*UNI|Cpb`JwO!~@FU_b-@V{B1uX6dTd=MGoyZ zLtDx}-QK#9cpC5S<+y;fDxxhCyP1KS5`HF{>tE7JL@)krboDAu_H#;11YXV@|IT-4 zugkOJ1`RQ^O?zS>!alZg&Y4PG1sB3F^XS{Igypl`KsQ|9mHrXk`n$(LuF8=(h7c6& z^^j(Vl;d2Dk=Jo*B*`!=KHnbW89F+(fa868>g+vh16TLXLkKY%DM^Nh+g7CcZg~#+oeHa6{mH3Iv++Mqbw1}S00ZGAzQfDb{(h`KNd(<1w2*!=z4CQW zwnC1#{;@WB5*AJCe}%BF+@8yI#Vl^8R-%&q@F=G%GODWUZ|ZV#W*y<(dH-WD{YCU_jI-KQ2%_4nEOeZYnx7CUV0DG8qJC&(n*H0{jGS>xCYtx#{SY z8TrXbARC*&X+)#A4Zrb(%gVJLG!P={E4c)kT31wEPHi=U+uik%lv_nam;Ljt9r4e{ zEeWeMt{ZO72q%v4rmp+MS~$Kmt-QYS?YjHf&h)VTaB70B#Z5Nz1@9>N2;5GNp>Zaz z@eupwV8gm*^5&}?{lL`It?BC6QT<+I3_iEXKPZf<+nAH6 z=^|R1xFEpa<<-Z{-Q1j zrapf40#{^x2))zK`D%y~_{GU8GKJc;>)$!P=`qlnr}<1-xE<6~ypV@E+37*d7U49y zTWe7E$k+5D0t5yhY?1ZbZpCM3Z^vb6(WttI%ic09HzxQ^i7{5KhEH`5PP2EXH-p z;t_NOUuSts{Lb=-on0g!-a9Rnn$Zk7d z?`Z=>v=fPLo_%Ei(;d~@H{hJzkQIw5XLy9N?4ftee8=tMBCK|D0#%$1Yi=4NwhBU)W)Lw<! zy9%^2lq2y}eg1ZEI$}4!Xpg)hc>=bU8JZ|>*%dUfXzX0+gXAa)i&CCWkapI4*WE-R zI2V7zrifDv^V!d)mOi|nKNIoEcBeaAScHsui)6KBPSkwZ=bweO`C)qq&GOsoUw3PN zZ=80z`0<3jl%HD>MZ4{B;!#~O{PD!{2iunvpXbP%Wu(3&?t5zBQroP9e|ck=sQF8r z(gq5?-`_qPRVt*<%@&=l1e;4x(bk-t8qw8w26iQ(@CsmQtjkh(VN^aMhqQDUJo9#- zO(_co^fWr$9JB-rFk*txPro6}P=F5ea3(w5LE>i5adppR&fJIDJZvJTuP}MWOIIrg zhB1)~7I2TF+~ftoJ|GWWhd`de=h~ z8&y;E^PRkueB3L@=U1PUcHC9vK1%6-4L_d;Wp~x_QOPX4L>wVY&S!Dz+z(*oBq@OZ zVsdA4Zm&d{N!Fl~6mieJSpK+5*|!V_X*xfCtuk!O5!a;PMHvnvM70{Z1)u zDLcfuH9rfF)}vTbQD(Ven|wd$<;W1h(r&brk-s^$8vP(^%ECOD{i#x9%e^I)-?Tm` zM@s)z#^_rqr3s;GYPDQc^r&+E;*G9KNFWAR9>x~}Shjz@-;wFJkBlVBjeSE-@3-hS zVnqEbIS`J`0HwbV=PS;_>>&AaH}}P1XaTJ_Hr-sZ>SSCu)Kj$NO^EH{`OhAjLLj`q z0^83WbhyRvax%0fWzcW>7&P~o9eo_G9u^G_Mi9a9>r8DveZlg*?qh6nobNt7{OS8X zYJG2x1NrEh>WCF4{rC`A2@|l6$-jdC<=nulo$}XN-!KA#?G9_f2;XVx(E7*=_dKp% zCK-q10~}h8B5KtduNlTn;G2pT-r24kKEP0*;_iOD+0<{pE3|5@jwYVVa za6L~tb?3nTo~wbutp;$qpH zzy-4J;l>{41=tocME*S$^7+@_><%-$i00FOX5DUBpOB6UquveR_j!gCzYjw4h37Ja zaC+-Fj@^$OPb>9(2+LYu7goV?pU~UvaiKQrq1=x7oP?fIR10&;+VVtLy!_nKiI?OD zKu}QUJk93G4juFxyX0J!V>>R!c>o!v#0xV1HPo|&Kp!nF3=Q?s_mJ<06|P?}UYA1- zx%(DSD=Qx9kh=gDX(8y0iAHlWA$0taj!Y5&)Aei|L@M={Py>SKGc)HljX*;8t78w> zvZW6Ff&APSlL@94?fFvi)oGhWzeG8)PAz>0h|;%)4t>!x?0ml4`-8fu7hjH|C_DDxKH1U<25|D}9v9k-_h)(9)kL9}s+gLx0d!V)tr=|fnWv`qb0l+(H%dnYXlPJVm>DBaIozmJYSE|Cri`|DG(F@Fm--^n%E>2G7$6tmMq0=W zEPE6*VIp7R{7c~Dj*y;Fs;8-e&;!V%KT8MgPDmu#?-11FtZ;vC7xyE4mEH9tN!xid z*HLfvQPj}rVm<9wKdk-~vRfvWPpyq~;7x_3SyttfIcC7T!{ksV~}<1kbIxNESXT zQMPVBmotE*hFtiV!EAxb;yR>9Zt(<;Tf`-A2{_cO$8_0M=DU5?#@;eWc#;jpxg$TT zolb-q!-cq6ai6N%yZ*jY`F4d~-F1Zm@<}bL`dx1$OTI0h_PHx*3AUvS@uR6``+~oL z=x9_9F#|mGB9t{Q3mBHzR>b~oZCvvuQS0xYJP`rwKV%eCIf2^s_{RME5(u?LIQx1H zeH~9kD%vr7OwcRg$GN5AvS7`G(#okRGAqe?((Ygp}~;oR*bQPrCp8y!kBJ}15RqR*{rsO=t$TrjlTL(hnARH+t$a`)Yb@JdW=43 z6Fm&fk)X}Jlj%QWYgWeVj3GMTh1IE)hu+cWSU*=3EZf^#QavIs*FC5I~D73y{P&Z*j#tLOPWJ zH=2VBqrs#JM_&8uzB)?R7E1GKOqe<o1!1)P0kF*;BSe%8=gyAqxK-N z^-5F&%>R1y#b`MN zd^51RF`2}pVWWJAK8o0b#Lz%VIZE?)L(-%)7W-e8k4ww;&K`Pp6QL3xeFNn%pM816 zQZBfg&W~H6X)HoHW5X@qx0smS&xeGVS<4zrMFtdMpNFT{CIZBlzfUbZXfV1_JvZA) zyzO!%=#|;;2a7kVDQhgalm)WbtG?5d{ONl)R!h5?v&C$ZQ@7K7Ya00HRbkJn)Pe(t zYS_dAW(=Bn2;-S)!p?ITcRz-(9xF>31RbbpPZ{sI;qTDq2IU+Rzxh5zF-{RhZ@^GF ziI@A4x^M(%1Vd#4j3kFNcdCCgP?Xbje<|fXs9EozLq#=FjL5~{GIa>GF;UUyjJs6& z@YW$~P{p(+_1iBq3GJZF5l6uXRUnhIsTUj{TMzOW#s?CcNZ+LpwbfCJZ zl(6im*G}xjYD*QLIP7bzr1ERBw=@#_cLse&5T?;?byFYtjNk9?;`2g|Y-J>=d7|b})E;&Q zm+iVm?m}PlKI1|BJZ{_DkHU4HB!Mh(Q3R<OwRM~do3=EuIembQ}^B4zw{DKIoaD`DmZtB6)O;2iJU2AKT! zsSNUal7;!1pk&$UtMX8HFNxf-vr*=>r33!ltdk)hFc;t@2>GGX6fd#Q zq%(e3{tj*bN&Qt3c&T3xAX@jR%azqjS9dfIVBvDiKcu^$Bp0cbz#m~A?jQfCSI~`V zaZtwpVIdlw`;>AD`=Jnh9m%VMg@NjHu*N2k1-VBi9Si9~a5G$6w`VWwqS{N>C7Z>79>2ApeM=jv$9pwaHW;3)9nGy!q!!Ul?R_Y; zaD=iuDygiL1#tHTyPGXim?-fqJ}$D_ty@5r;hh-;a3?M#VGmP2+^Lf{fc{MIP0R=G{uH@O>8M2M38#sW(m*Ck+){ygPs6Fs?DuGUp4J^Np037u zCtGK&;ZSzu)GpqzDzv<~Db#iH!oIw;lI=G*Dpj2~rDAe%;3)SdCLF;7FS+muXl8xn ztk^k4I!k(Zt+uM)=C0Rogua$$7GCUt!WUk+;1-om6w#htymgjw-pYCi<1KUryq#Lz z7n4w28A8(Va?#h=AsR?|)-N0xM5US~2)@sr>(?&2HQdriG^13(of)K5M*Qx(MV&3m zIa7xwAGfwq3pL3wy%*M5{d`iXCcCk5n|D0M|Psqn3cUEDwt4t%L{rs^w2E9hDU zPR0kP-u9_^E=E^BA*B-00aEFQKV>0WI$1rh4Si|mpCU({6a9|-5m;Rp!}EZW-!U(g z2rH@hz)5Q8U5AnonQk>8zzD=nWgA9ty@9>1+!_Rp!*4b~N?rTZ?;iA=n+c=^W7i$q z50IJPzvh3rb7nv?V-UV?NP^O;=Cb?yVk{3MhLuVM<+s>nf8vgo?JK$o06c09o3BFDRhMjuH@66;!weal1~dx@%WabE;m0j^Uno3uCb&(QGN(7C@U zhNg4oAyz|FIHN%?*mJmGhWovmE1>}_pI{brK_^wZ7TH3`%~Q8Sqm0b76QdK5?vz=Z zlG5G#lY`ll800opf$v8d5~imWAmObWPetiIA%dB>h~k-Ka8$zp{iIQ zr_%PePB2iYQg3I&*b2#hd7`OJd}N@$f5#nuOS^0ejkVw@*+bus3Fpq{I@!ojM`7g% z?2y1wp<5j`>AT>S#-(kK7xY&syg;U-&f#BhFAp1$d1Ut&B`u@)b|CTtVMRxU{4>3t z{%RZW4of+Es?w~pSL3;myp-r+oD@TUFg4u{imGsR9M41{>bYUuWTp6Ob&dHMMTJ0N zffp*b&D&M@Nl)Kch5{c&H{v?v5NIl?NA(zk&-oW#c-i1RruO}ryF+tjX*LProVK5n zt!tW(cU^`_GfVHcwL$u;He<~=QjlK)-Hj>kDNK0D$c2ODIQ?LN&3Jxj>&%We1s-(*fm^px7YE81hv7#DoA@(Tlp` zNfN+o37pi8my)#!A*l~?ryFTY@D8Gr9u@!;zY7y9N>qtb6WF}9-*cG2`@o$@8vA)W zA0qnQ5$WOKG?>pXlTGE`n%;LEMT+nNEvcJI`u#2K&7yrxQ-!FouXMh0c0gMIcG!rUdI0UgT+u-bZ@&57uMWLGMX8<@8_o zb*MY|83>I%yvkZ#!RRK~?U~BwTex&7M@APaer?jMXRH)#oxk1iX#j+t`Wn{LL)e8! z801Ugj}h*!2*O&(lVaX#a9)+=WffEWwZKn%cKd_+bGZ!S`6&AvXYv~yk-{fn7PBrl zI7?(4**Z{cZBS8tj=A&pK#f(_d_T2GNvj@`wqlWA>f!+eSFyDiMuO}S*8l8Cb8=0h+dX%^Ko?-XW14% z(hiohHrZ+t|6>%u;+1k;qP9N0`rV!7%r{Lc!~M1VjanJkE2Pl&_w0$g!n7O^T4cEk zbezR(zc8jv#&x%>-2oAunSsX@EkT9*m{6u z&5a;^98f9v(faV;E!_kVl^qCt6*F-BtH;EHIEP9QWj3kqmezoR^RGzku0`&YG!pfl zWX1`c#&g=*Ds*@qKe8{jrS}e%j@oKBmqw~AIjjNac?$J2(}2E13v|ilxtmy;u8NaK zx+Of+iePe8@V29k5Z3WJ<=kz_0;^^w+5d+EaeGn>FIlB8B-iha_^)^LA0WuaQsw~R zVH>17k(~vDj@9k#%n&#o9wKvIBSK;X3rzWKbkd~XhiPP4r#VR@foNOj)R5Mq_sr!&4`eUM6Yb!>x(uh2GKyI+xJj%7iLNMCM+GeEWX zK4L`lH{8v%m|BZ$3HvU%)%`y%X8NcJs@TW>E{H_WFNWm>4~H}T>zy2Yw_e5g^xq=G z*2v#^xxSs4ARDam%5^z?MAk#>8@W4eh(GW~&7E4^l-dNMauxg`Nu{?Rd?^{7oXaE` zVqKWkORd>)8%b!9bL_K5(5rvi%VM#t0V^t zDYu^V%vn1Ev*b#~*9+vb47O+u`KDq^=wYGn1aCjB!# z+C=rDi8xk=t$No&y_}RT(hbW}>B&!hqIfB8jHYR1Gf_<(j9hpzV~2X12< z6dHYwY)DA{T+U|d#jk!+pWl&!Qddj&sNOwh_NMmP7CR!Ls48|JmS~00MhOvHkJ(?K zv|pTe(+9sIxl>xCt6=gshfDFTT9sFxMsi{1zHv<_c(APM=n-$@^RTkKi%U}_r)L)$ zA#if;zJk6>jP3-@^wocG8b>kBy=7+kPG+9wo^DO30ikzB?=Gi2GM;oyq^j7dH{F<4 znJlv93nShBA^6i=6lJ&n;2>YW-r@Ak+3HDq#c8M(Zw{9M3Yv5j#y-lbGS8@>>DNhJ z_S>1)J4U@yy+gW3Wc?W7atlRC)E~0G5w^QlqcH7$NT5zd(7ov^uINnx)BSVw#h=cH zfEIMD8gMXI2@BQ*U=7)YkkaIX;?T2$*f#I8{#BzR#$y@H_B-x|A6Xic=ZIMUI|6pn zunLBm)p#v5l%!?mB#)KC%x6*awY7Ez2bJeN%zM}LD%OW~mg7hf;Q#|c>N(kKM}1K= z=?=}PG1p;Z7TjEN7uYghbE9`(jpi3Fbral5EWfI&$NJ7@TmDH*MuN7)e?8ush@)}4rdVP#hucHffx?@_WQ`LeLpCbA=`FWz%;LkK^xm;@yE zVUTvm^j1JR;|S4D!r9uoCY9+84Fl;$z;8W5oyD0d6i=Xt2_-;UxO_ z-oCe!w*t{#{_4X%~Hllndb-tt(fjhbZi^!dM8B!d*V!V^&bGc-ui5YNkuC1S@E z*0V@5hU}ZNHte-mknE(;+i;8BhpqJ&S+%l@OAO*at82?w^VDqnVD0Cm`L~e*tychL zvJf& z0#i#l(gyi-3yZc-+anPP1>UIj5BF!S* znxZWZPmcr*^0b5K)PyII{%R+up^zNzyFqI45YjNxm*Z=*lj_G`vqQm}aecH?z9*pP z@}(+=sHE-L_rc$yUe}VVW|5yL&%YfW=yg%%Pr2aS;%+}8e^6m>k!o`PaePbHC|$9` zlR$`**oiilPebZWFMVol6~u^J#rDe`g0;foAD?c`co0_VUeU9%?V$It?Uv*_w2h^i zR6fKMJzxKqQ0jwFu<84<#>XDchbk-zh*S8oVnu&%__bQ=Qb~?ea@*AoP1Cfv^JdsL zn|$EDog*AeRqi#^PM9@8d+si+(hHBY^qa96IjYSsKmn_D=P6gd@=I;bR*%i25HH-v&G&T6xeXQ za9Ufw5$Dx5a?$u1=h!@OZ_(y9>twR}yvE>i_gNsl{b3upbITD<8Y%GxiiWl7#LN;dZ!chDB@UZHC%OIJSF$5MD9woc*pb z?xEqG=%0R=eKE*GE^B+1AYK6YvPu_mq+9aBpT_IZ+GU;?+JaY6*ZXcKKKPm==i306 zZllNky+RwXepec`^Sq~AVgB!;>j%~LI>z|V%>RV`*JTs{>!p)MK`m==Kq2dRRIZ2? z_%%j8RFLMrU_COa%3%g*dY5yz^_z0hP>_KNWk3wH1x} z&)Qx?&;Wc;1CNq4>q7F8ev`*P@3tTLk8L+sdG#IYzv(*6zbT2NL`m7~=${}ZclX!Q zH!t|(4hBEnk-99$G-Mz&yYsNNTdQz+$$rRO@~`*tUCKxksnKzE=G@g-OTbu3;+&f1 zFU6)Fs!Ie{H+Rhlf62T(%zngm>Jiz_$otMsRUh-?e(4k%5vo|t`PDQO=xBgn)g`^ zENVHyKKB3eVP~`1(i_v4*0!`F9GmA=q~;a#x!F0iZ1_VeE-Qy4|FT)zpQ&da`G^k9 z3#j+MMG5|YEegNm>rADnWt*rU0*z_|L5hpWoMbLLYZ7HSRmqyn8r4rl(+wf*L+mX{ zLgiC*e0Qb!X{e|2#($XX9KOdREAwbykcf%IsDYinh z;qB93b0Q5sThugeC;#zsAqgzr*_HsMK(d%z`22D8jd1@tGDX`Lg-)mu>9kL=S|GPK zF#MY%|8~cfyBnXKq8u>)#mLl3se|822-S9;)Opb7iJX=D7#*Lo&m;e04;6(KpJ$nx zui?bQ@2Hb2*04uinOo25@6JmqvM||qUZp0R5v)pz-56%<!H(wiuUw|Rj*YQ&kK8=O>7=AcfS)%wzm1~RQH>5-FzWbm7m=;_)*I(ywf(<#Fz_%03VTLq^ z5r|M0x#nDH=(CmVEr1?p68s$eocauYZsS?k`En-r)q}V=1JT{9@87|i18JsXSz{Ns zR7Vd?_xB0`&DqHGim+aof6g2n!8y5{`>kqwCxh&kG5;bu{psPNPC%IhVpZ->K@BV^ zy%h+I^H{#f?S#I-9Ts?Als~>0;;4}=uW>d6vM%zAwalml`ic`-i_{Ky*ga#`QLB^+ zmo{c9>pv;#Keek#2uLhG%|RZQ3WM!YTt>^_AlfZ@UiYac0yN^OZN|p>TK>GOugp8zBlU8eL5QIJV)1Y^?3J$UHr2Q=$;JX_YrV6=>^x6r=dp7MW zAh*lOhL$#;V~*N=9@ME_9-PaCnJET?3>|eZVqz2B3>i;)@UG;t%4w#q9<~|={&iSx zy}NE1&CCksk8j&$rF(7EFZc~Z^x~yka4%=IAW`s1e%q0G9St_-eF(@QC5sxLDUMsW z&cg~EkmhKg6Jj}}11UmCAZS{ zkt{i-cvjktVn0_9lKZIwyO@LyMpMhdamhyOb)9wmj%zn*UD@rGK^okR6jRb?nF5%HwQ|NO?-mA2f_4^5axAeNc8WPos9M*kfB6S+0S~ll zIGm8=>Z7|SZfy6oUscj7y*laaf2?vN1Q8!Gt9Cs)X8q<_)7SxpvMX7GtJp%C(7g@Q zz?T4qa1|<1kH~0d61Wb+S1rf^eYas} z4#wg9w^Jj@z8O|ZFSaEF3?Ecg{~(_TyUxri$Nc(gYA-RjT6x^^y2$P|ySX{}J9@pr z$||q!(Zlv=5%7wfFm&c&I!EP|R@y9*`7bQ4@f)w8zq={r; zJE#q~PCHVG)Q5JTX#cWI55E1D`+?Q}E7R^_!+6aQ_FjKkRN}@0yGi>4VusgoPWO~+ zGk>l^k&7ioHll&{)RS%)Ho5-AkGdhjtw;EDc^r!1U>XQ(GnoqU)F25NW6`z%w|*+= zdkY)-jnH#j=(_g}S7NX{(=l&+%a!mPTbePy!=u}0NR!~Fm?)G-dm^MQPqYY=Dq)C!6`Fe5`mlDocP)P!Dl`3Z%Y~D zaR)ANP#OI?gj^2~sa-3b+PNUxOpA=uRav7X71+wwyB`tl$|McjbXFZ=U-2>O)QCDq((8WD-o(Fgm!o~XaUOyUD? zROU1xljcd{qlT9%p~TdmxW%87?N{3b*f4WN#3g&)^;z2`%x{n$7F|y3jqAGfevb5D z@x;9(B|>^3cVzN1cl%pvSgOz_>f4 zetxKv3DmS*svRZG*3PpZUyI$0{8`7&)^gr! zrGDNc)CSH(ZYNfKLi@9XYGH;jOP>~*X<=}~-((0n0QPlMq*8Oc>A`y%9J7qrD7DiV z6a9nW8U9NA+no@J0dJK=+d-_TlRJIZH}P=c2+8wEc;_Nl*Ah9q@DeCe8HQ&f_Z{0f zVcKB!g?jENMZC+Wp z2mlXoM)@)DMc)<*yA>k865P|Y>^?-;)xhqSH)>E^V#BJQyO|{I;LZ6*f9Hs`VEnKb zv-png7SXcoy9_q}z+~TesZ38BI+uxGM|YF0^$H&Ea``P(W}jg~MZW0iu0!c3+ORCpTIct3sQCvMSA_O(2ylkOc&8h4iMu0F|)hPB}n+lV73e1$GS z)9AcJ-32gZ)?Z{!Oyn8sl0#?q7m0Dv*ZuS8JnM-OMiJsHp`$f#=IqRDMx42p%iNmj zlu15A+{dti11<*ZN69xWeu-xyIu{(#Pftnc*A^)!zfcX8+26-ED+n{-WyKZ|Z z$|qSj7ecj%crt6hn^U=Q`xUZpBC=5Ph%q0zeDt(2^1wFt!byi&8bLS#YHcTHl|pACh&u zb_qBpfxIg+p1DGJDFhy~S5{NLP4a*JEl%`8$97ghI+}3I?acH8jL%Pl^EvU^zCjmK z%fl<5xw50M+&~a4L{G)In$~5*=uQ|{quzdRhR%$A%>E|7YSn6;uolR^9$xoMnLX2* zA9}PQjZb%+8|O8_dkeg;fFwNiZxU8F{BHcGGSnOwl{I+xc@(4B7ZeHOn7|9eo{h=N znf7AFVU(lvd+2&8=73V|(xDxv`#2uva)s4qyIjUL#>J$@p&_352L7t$NTG!g^4F*$ z$t0R$)v=~)g6VIZI)p}Ow0UxwYKzW{<@)NcpF887`2jhU5eX!YQ{px?9nU*DAv6lp6d2k(Tpc zk@7qf)Q{A_JW4}*ee!Cl2JwA0AivgOfIL!LE z*k0qY3F5sF*gdRJJ5y>~! zs{mep=b8!+YM}9V`Dj4yf+TNB8N>PI_>xRu0XdT8yVvzjTJErnK9F)1v>r}G6{4*J za7gyXN~BdLITdCWAXA%Q9xRN^VS#;=+7pytqOPjZjVzxcj8H%dxgrnm1rQq7 zr`ow%{bwi*9GD|DE2F2~R2fNcUVcp%yfx-0`IyfgcC83f>5p=k>8hYN%D?cKCiek3 zex$L*9Wx=jw%pa_I?`r4M0pr7b*JKv&w-{LY%zB6`?fKd`$~sKx;YAn#^b)qZfvGC z3Ce(c({XG5alE(8iv6+ha`8`_CvF92mHivFeIT^2@ zzPGt{a_UJOn@V3|mq+X^X-C^o$#F4{YQ1GX1uMA8GULo)J+9xqqYr&+AsBVmpyi|k zUe4X8qHxNhCy2aR2Y5AcBxfOon|jo-Q(e?rX|)$mYaEhWiUs(R4Fw4sSG?Mt{-8(B zC8HXg*|?p^CpSMX6&$6Zq@}=a@4y85`kxG$onVWZz28=@QC}Rad~nx$q&@C8^15{y zFm|TgY1{J&y~Pc5d4YS-fOa6RCE{F0O1Uup5VbRb6nB3fAUY^yUD9PDXP`3}W|84N zYxCzJ<(nbn@loCUmvIuxJp0?IrSm-?H};){-OGDn8Bvn5u7_7>}!&J8sEw9g{L4cVfpV%n++w?SM?aG6Aw)>>^-BuX9|BsUhED#x;+ zfI`mju*6p4e%MJ%4VUM>U1yl~I@+dg>y(HVlTKr43!MVizlRz7&02#CGNv#_6MAyg zhnWL0L&PSs+GJl;tlf@AV!@7P&U{46O`QGLZ&58Xq||ZAnH6)j8Q0ZMgP`bLY`+Z3 z*2!U)rY~>mi0`0cNeLRPDvGe1!SSR%;6#PTqo=0WSc2P*hqBa$ouV%=$neihoyZE{ zSNh@gfv#a?kzUiH_{kJ@&EuPsnW>BItu0(;( zj@Q}FPsLKVt-4}nmD;lwd=7DYUoAb)K_xby7Ja?+kncyT1EAglbjy7d{6oqSLyFDgxL`-&n`yb78wsD)3{zeIKyDi1+$_82imE*IuR z3rMuQFP#N@t`%;-Y1`p?O5x-b0;VM-iy?HO^#`yvB9|W@A3`vQSVeAd{1rmeF?e+N zH0_m&SZh+fS#DPoxwK{HVXM77!{I1N@)FL2~qJ*=&mfR{FpSaqmUlwaj?mpgr^imLx^} z!fdmWpocYFvTa8Rbya4Hk|A6L$)y=bLA>WXX$th~+hx%FC%UI`n4r&Zh52&HAJ{hw z&5K7bU5|dzNd_n-2I9unI1lb}(Tcq0eoPRo6qaPNdmOgTv7qwRaF#7p;#iGE10K?I zVC?EO1@7fdatP~pNzc6Y+QNHoZm}WF=Ga` z{bkDMM9!+x$-}3t5;P8zr||vaW+hGLu%qw>B~Y(p&#<@iqMTvhx({&A-%IL~S_gmL z-E~&?`7YAK7tCdR#g8I^{zeL(ex= zOKAsfD($~1JRF_Ql)UTy(qCnWQx=M5#seJXvXqWqf%FZqB5{?H?Khwg_SK%lYgFL< zkdIhNQsK0?rWR@W;*yqjD9uuBIHJc;(!J%TYjNY5)oBX3G5(@-=mr1f+IwHN`iU$& zr?fJ%0+>v$$SkLVfCJrQ4F2HD*#wD$n^rw{#IGG~6FAzgd8kbXRkRL;ZI?(1*f*k8 z&U}ECcnXDmebH9yCvCD#Xp))u?9MeJRNQEffelkPc>u$EtY$-h41_d=79siXZ>4+p&!SYdnkKTg{IvVhH~WE8bPr zlm~8Q)Oph!S4g+ZhIMI6$tZImS}TV8bb5_~TaM}9w0A>wBVRu0lgTskWSxuH6ET3u zLoi}_RwWAvQSv)fFuw@qC&fSiYZ0a6=Ct1~!2VzPw2Kzu z-2Xk8e$*TY^H0O1QdN~-6G5O=K6@UXMtvx1SfyBh#OMZADkUhdr%{sL^wXS??70gK zgHa;z)(sL_g$+rBTat=yt0suTlXId)vVLgg(nQ@AyJ^-8PQM(~j@Q_uIj0-=`dwA6 z3))y7al9S!!?8$SA6qT_v~;QU8D8(G<{K&SA-4a^ps=X-c=ID*2oKrr9vX%azaaB=LMn=1%WL9Cufz#gC;rl9nomRj@9m#5?T#x>-)ET%BHfs{%5@rYc=-o@@~JyKw;1y@qfH+P7&=ct0j(=0wR{vv7p1aw>Q5ws!aY#wCUrMng-W@v6_8<9me@siIxIIZ_vk{7W=Tc}^< zoY5?wK{U^+V4<$CAdjmi+Xq zAZx2r@qq$jB>J=}*`vfiM|9~fKOZj0+@1}xw0?1r31DuMlrilNjafx}%&_VrcXW8l zz6$;-V062~hMR9Nx2;Q!U$IqCe3j03Y%&^vAwOkAW7(Nawf&d?4^2RcmuctS$=e1# zov?3hg$*$sibWPxAwur^`(L;*Y>bo^HFS;0Dg#{OSPL9zygmhWO8+O+CQ;PP`ZM@t zTL*w!=x|>AT^pzWnYfSX>f8P@e=1pn^)4iX+~V;IWMv`Hj`rEd=<=5(%Ny05s*M%< z+N_xC&1mS~e#TAKD!lv@wM8EGJ)v-E^-&{MSB;_uMnyq=YRR{E`Q_o`M&n=ABA4&# zVcoxf+n0}bo-+5;tA8M^?rhY1g9rj*2jDKYJr$AjtG-+Ij0$*~wcTxQ8!T>coeywp zSyV!b8*GDK?(_J+yG5h(ePT$MvB^5YFpNcQ5ly)v!E}}^M-=TK)Bgtif5eWsoR*mF z#xpYCIYn(TSI@JyU=j+U<^wA*joO+x)?c#@FkF{n+%emY7{CC+EFWjVHPjg>^tlJC=_D7NU0Xekz4I()w65lpCm z!m4bH2Rm+ij&%PQEQ+K0E5oGnE;%kj$bMhQaY`U24~pqT@I@(l+?Tn`s3N}#07 z`2qA|)tlTB!V8p9$5o!0xJs=W3(UcIVko97XrEeDXp#uVi>h3MF7zmqG*Cg`#Kmq1 zwfP-pday+?&l?!Y)h^R00933mLDah0EusP;Rq>jHIc*@BH?gnR&JV8GkZk~_0Q+?$ zUANpFd6T-<%$hr%`HOZ(Rr#sqx5VpB?|Fc&*^sPthd|tiJlI$`5TH9gmfo~1b^Ll? zg$78$G;o0W{^tw2Lo&$$&tb=(;WJc9zj_Q`Q!x(KQ!^28aIR4p1JICb{@Y)>YaB3N z?*+C3?wc(XdX}BQkK3EUrBlhZydk4#zq)!=DMO&WUHd6`Z~2l~_BJ z!P9M|tO2>$_(G8}%$aV#NmH-{LN9amgQ))eI;`z%L2BF1YGz{WuJ5~MgRXg<_n4?a z=d**n8HbX?Pgv};^UsS_Uc}j-hbI$1luYTGm=%!}^^{lX-E#it-xKj7}|xQXFoZ_-y1e5liX;6qPRr6ocpwXYhpELQKa{d z$MF5|U2qW6{YVu(uI=w(;Z^kM27E{SU9tT0Ps8Q)tNmR*#=IN1Pem3Nol-bbe@K~` z%6+4_W#yjQ5p=u!EUfte&!GpCY~EZMIZsaiCTg~>SCtz|$*kz|N@qH}*B$1i6W3mo zix9E&OuYl37IS@%-Ord-c*2_-PjCr*W8JG;Cm<>!vRmUEVk7 zt36pU7%rEddumN}nA>dQ5Am2RwqAvFv^SD>?|LoCsCRLF8)ou7tt+5ew1>sMV(R z_AA(17RLC7NX)-=l5zqv`)bj`buG*nM5;kF3;P~mL;Vya2_HJ_cF`?^d7Ddq%&5^L z>Wc0c%U15^S@G(+Q&~rBpYn@4o;#D5YK@H5Sh{nAGD|+Dsg8K?36m9!;W^PBTD1}Qy^nA=UK&cnli=+ud=QM=9jM8^Q> zCIA~g5j2X5fG}88n8TcW8n&7;xHqZ{syrKJF1Au@oBopB-eB9OejrJ+AjAh7ejg;_ zQ=;{u1t0_G?E}in5X@Qs=7TvJ_za8E5}dU`lYrb(S_T)bD(XWUSMB}TNsK+q_VCG z^1jbDe)3iQ$q@WBT-HCpM?(FkbWv5!?+G<>f4&SUK{d0d0JIylz9%2{MuR*$ns4YIE)(s+uR4=bS)E zcY*d0?mv}F>3LbV#wu|OomQM1GSE{oT%5y8gcB##^+vDu$rfI48c(3x*~<8|Ja{I^ z+GJc&a}0EL#wEeR+BtqnPX(u8U^&k8m&@_?TwPKMkNq>$HP=)0td^zBBFW<2H=5F= zGz>YGv~-8ccKUu-ovkkt?4s*+c18fg(xSkrC!p1FG$R`07^3VL@8Ef4--gcu>#STa zh%}Ck9fpxGV{&(Q0QCXW5o@MMtclHLU?$Djilv6{89q)^bC5FsLB|u=vT>|wVs#{X zOcuQv7Vb`m+22XnO|uyzUV&*^8oLj;=Ly}BwyltFsM0D5iuL2V7KM4w@I^Vhs4zAzP3>p9?&ih9*ed3 z=g8z{`vXJOvAhRwEePx4*8RgOyY^R&f0Y5;dELT}+KTld8y@>Ket-OQ4;Eg1y122z z1t~048I+U*!&QQ=(EyS$6ZGK?eN?q1;frc=Zc8F;nCtL2%cchR-!U3zbGCCcs=(5s zad)}G)BobMU@$L{KfqB)v^2S8Be;!#BP0Rol9elQ_^oXMD;S3ziLzs~s&Tx9POZo* za_|=F^g3Kb+OE|e2iOM2xk$B`b)~2pB-XGj&kUjbw!yF>YF1GI&MA%BhpH7HDY03P zAlC)#z77A2uaXJ`NK&A^8TpHEw-Pw`v!&XG->W*V<8zBV3(DY})Xf&X8(KiuzZD7q zQWa(@6b2#w_xZg))4n_rc(smdbot_MAXXJ_%L8^IAo(X+bxBzmm-V9buS)hmlNB7^ z<8{mJbyeE6_wyb-jIEG(Xh^Uy78~T<=r*B>$|JZZwCY>fEGxf1p7f%O72&_O2NwF| zj|+XKh9+}3*~*SGWJ-H%IQ(fS5j8`S-ZQrfUQX~~`TSKR=s3yHuKrNpc^ELNf11>R z4i-CD@%^b%zOAX}WYwa!ZA$;L2T#taLPE8os~4mk0=S>E91 zq1_6^=m3J!BW%PQHpx%!qijj9B5YHe2ix!3-|g<@MP&&TipJ?_z{KzSB){He= zfrbRfbz>-NxH3QASs(s{lpQPycNtz3VuT(E>CaXef8^^*LGJxR5(dTM*NXc$2~hjo zV0(`ZD%lm#EaTA`PlJmmGbC}|5znD}>#+X*EOd{&aV7?|gm(Hb9IuMtwYxWZ$~)U0 z1-^r))Xt-oL(M&n7K8S@*{^!-PJeNlEUpu_7mG(Gn~zDRB^ziz>S5lMCWv?RzmL8K zqdYwx`(-mb$^f0RisKBmlnUC+f?qj&Y4Xc(OP|DB6~>zjuX0ZJQ%?zgJ05NIS-+QQ zj__u^%^;kShZtDr4{ztwRdaME*7X)1zIdeE(Nj9A=s^p77#nE6Au@JxN zU6q|CKXtSudJ*k+Co1;AoCY+OcdfqoB70T7zG^&O_Mj%2ap`8oM&84u=3{+fOnjYQ zt6D7+=~DrO2D`n#e11@-;DVv-8Ic6|Hl~#rBA1CjT)+yebkYV|F2J)=J~2 zX!KMUjyex+whE`V3Zo@SUP~>w)_@ zi=j<-RTW}|@W``gfJc#Wn4-X1lpjdteQ~1p&7oT0ir2?I+aV@pE-7;h(i5+;c+aqG z-+n^i(v2f>9NlN*`9DPn=R!A#+;AXaB^_T`ylDHvb=|1|B#-V7v-3z-OxTT6k zoWJ<4b3+mssHfbTvs+TA0?6yrhYO0)X5y1ANp}o9kHVc?aP=DCY)baDRj^1WOOh9E1 z&e!-aMRAydTkMMkG3Gpz>_}jK$rZ60pYl(A$K6T=-v;#MC*)9{YV{a4@bA_tl===X zx|MqQXw`vyE zY`~PmN21;2b{7%bA1glZ(cLI~$TO~x9!3O3&Tb1L)?sgjlF0H7}VN{?vF{|%ZX(F4TF#4{u3qKB_;BvHu=$XB%AI!Ux< z1e3P2vcpQ4Gm>_xd{*?$4|z3gCpROLqXACk#`&L5iULc7|JP370^Jh{RJCc=pS zoCXmy|NDa;`5t}TYCglgI%0|_hV3}^v@x0Q{|5kLnNC^vkT0C(yp)%y-9|O7ufz-p zmihr5YVGA#DDfJ-(jw)`i#xePI<)m=kD|6-QSDEu2-}>AAm@C$(vC4102``OeZi~^o?6lWk3fz;J>H7i-uN(M11f`neAZXUo;abm@rOrpMjQJPyZQX-|L2MRKk!5p zf&_virg--k=fhj#2v`S8^9((J6y<}Ryg2^_-FgBGGf{zO-k_eqN^n35hs`iQ^MPH> zKhOkQ=h^>G9m#AKr40BW;P_kBr}in=;x|22;PEaOO-I4Xi;v+NkatfX{~kxkKk(D& zf8Zwy$Cd3&f6jKVN)%GPq{(%vT0M(ti?J$ z`6d_zPw?*V7V!sSjB@m}u4bxpW^KlC7ccEw;0Yq)mIzV6K&FOWSAW-Ek+r@K5FXxM zi?_bM9Z?a7r)@aq;7;+~9W^OBEswFSk*^bLysFkVN%Px+ZTTbaG6fx$7YFZ4UKrYf z53BTHj~LP{khz|Mx8@5neMfz6M!U_+Wk~s~i>HjfZ)`yke)dQ8$41vBCAA9^10v_3 z=gVeagtzJ-i+B!S3@R|~dxQm60g7*DjVi;a;k9$0uz!`YRhmkJuzhW^Vuk;20PTzpZ5dadnx zceazIuGJ@G{ecWc_956BKt1eu@Wsz^wf>d9;$o(J&*1A$dRphWyE&cOa&IQY?bVS* z(yv5TSvzC%a-2LM$&35!x3xVdQmtjc#i6TilXzmT|DE3TeCMc&-GdJBA;`8T>HnmS zW?}-}N;8UPbkh^m%3i$|O&D3Il-=i5b(eJ;cY0U@|r z5>N@)?M=G>f*qW*>ppCO#cLtkKXei`eMZ|nOI=$VYnCa`wbaSX@W3V35$(~tc1UU} z2VS{aPT#hQu34zN-TfagZ2yxsJ()l~;jZwBi^kbs zxQb2g^Jh$v!<~F_Dy0%OHNe)B*LMe^F7akeRsGK3$wWVA8n$-uN%)gp&w5XG2ko zoF##cCozd6kG1&=kg6Slog;b)lTw^6b^6(PNjY2}xg_hW|8hxfGG2=*w2Kzo(f{R& z+D^5-T15nl^0pDfG2%S#rO;EiAm#;SVVn4#wi5KD+pH?O$@l8z2wzf@3OCujdzwYlp~_ zlN+x>=Ldj2Yiux#8%0fFwW#rHt0G@Jy?@*Nx7JVaYR#7%&+WWf;b3djd~G4|{w;QU zM}5A<7iABWmaMIDb|!!^MUCIwR++f-6CcU#w|0IST%alLvnrCupMcG;)BV`#c>O~T zn>Tc_-M2@JaXz9nYV9(Ie&;Q=8%~u6T#7tr`@umYkE~AP^@AKn3wKgJt4TVAdJfm{ z(Fpf&lWFt)p0)G-;dO)kVHI3`2r&lY9x|NzlkXJMlKR*E;rtYljBVd&t2%0SrURG{ z3=D98PC;tu*~{!$_5QUDndZVHGo#HMv#NQ9KF-DCIc7PQtB|iYy<}IQ+4c-Q|Cbs_ z@N}^1_V-&s8VY;Fo$vWj%d)d8$Fj)b;NDat@iK)N=jl34wlB11Xn2U}>Y+2yfFD3) z+HC4!jFCR{s-#sk0a02O$(<~XBPgYK)j3XQ=YYyB#lRmvDa2_YFj;&=mD>Fe7L{Ym zJ%RmhJ_&e}XNti!JImo+3VY*43hZWGWpKFV-exkA)Op#Wn|(Mg+ULi^9nF1rbqj3U z+CHJmYL8CGE3-fVg$lrixE~^G2NrDSwU=xTD!DGHTbOS6a&#*PxFA{!Df4W=pf55F zdbgYgKzVx0vcyOTofEIk_=L(7_whVHcY8njtxaVPxhmzdHxz94HN*e(^Y+Q-xoA9T zZvdB?PJU|Yh8 za%0`Pv&Y;Av5;E#WyDA|Zr7p+W}S?uw14|>`q9f2&$8yL(7(G!2bIHC^Id1R@ld}j zSYo=?vvqvmQ5uN!)?_UIE#o@TI?t+krf-ex?mlM3F8j>YNj)zn*rZw-ydD9I7Ci-( zGSi6oE?$+f+tMVPM9u062ZqH_Q3uZ@`aq}7gObihEtb9RL;*_Gft=vP{iuFBb{NBd zb$0%^(L(Ib=I8ofl=H!TA98cS^?bOh^4u_k_3e1GuTBB%Ahl~BKcVDhy6jRlK8O?1 ze0c>0cqDN*xbtovi6ZYg_T^`|=&m$?Z$YVA5?#ymz04Y6LwyYErHy}+Ash(6WxaYz zw{w8QcOBN>ly=V^m+xl~IxHkMeTEB;HoFybnpSkPfghZIGY3qvl3@oTReODJ`BE~& z`?UP1FPk!#i(%>IRXg{;S^`-Do}T^f7GoQ~>jCgi9L?Z4d!tqJ z?QN=Tg$UQ;?SAP8x&odM#QD(pP+#tGYND-p!GZU2xk(Nmy~yjiD>#g$lryEKs|O|j z`xSa&`;CtOW4XER_*gLVj34eevpY&D@0{QCO7xIO{Sg5G9Xp~}K+Hx3b4-!gqcJS45qo>`GWXkw9A!nOvs+1~^We`%&IlLBIa8D6u?(-rk)SFQ z3}8lkQ9eJ+#fhrfu(I}6&&@FoFY`N!TFP*@tI&k5?;@+R;i#FMyvW)JeBh1SXqiGV zjSmg2%c_GK*7mvpU0B5)$_PrT%i&CAxT|s0NOOeVh+E}Dr<4mI?X(+JnHMgc|G|bO zmjC*0%71wR$<506G?d$yQC`V;fHPI_CZK8HH=US8weL+tOzvOO!SVm{KO4}v zO8;X!A)LSawX7ThS7X+(x6^?)>Ht4eiWGIpf7guz4|&+bvGP~O&igOi z-1lkKAqH0jhV-^UK{##;565f`Q5yLfx$rO_(4zBlOPVJJ&Cf`VCE8*|%3BcTym)Ez zk-FY9cldPVw6pE+QJlLh@+Fa*6XM=goPWq}p?wyG*qbi`XEPaggenLz?~SY{k~CSr z_D#H#gyl6&w#lHMo{Y;dZn4{O;sUDs+T7sehZsd9{ z@(r5*Bepnx-l5E3Q~DYCINV_jFoUdrnhC$CPqdFaysKSuG(bVxxhS;nBAv}z z)%zf*Sg>xee{Vi`JUJBY6ZRl?`{=cT(BeG?q^O%iBZkVd=Td-@rz084AozY(MT+ zjdfwJYWxzNwmKLW`{bIjaXYXIXR+kVf!aPnxF6iOyZ2y;>4$Ws2?|^WeEDf>a4uTP z88Lsad|sw)8ujfd-`BnvlWwZ#0$Y9=@1Vzz0hL!9%8bYB0^&kU__a zeC=`8IIba1a2KAV`0Wj3gN$4ou&e88+Rn6tp2gS?jW?%HeoyYFtU^$q3EU|9jjqHt z%lNX`s!U71MJYkT%2-u3;F-PP&ubac*s3PCKlVK_iq$xCe1w{iB-L(+Sps|ob|$!~ z{AYJ5N49Td|MEC+yQt!GZ<{K;zTIBQws>WRX^F17{@R@`;FT{|J{y$e;FUj`rahslA<^EUU!xz1Q(nFxV$Qu4>UFSMIJI6Oo zhf}G^R5kNlFh~8@iDCDUTurdBs$npJs%Dz4KwdhG0jBC9Qz-iaIq;6iX zYhfk*2He0d`;Li7JYcS4Wye?4hJ0xAnG#AG8aBqSeCivKKzu66?{E}#_y*$MaueW2@f~JAM%j%K!yJ|&x zOfEi&?ZmbxKb;afaN+O$HmP7&+t*y@FL%?u|JLP}CiN%O&PiM#?_y4%X61<6wQ9wD5NK14PDf}O83JX|Bn$MF08sQC>^ir$>A1S$b# z_|qgDaA24>PsMLRsRzK@f|p$zBE19P#P0dHM_mu&0m$TbYa5G*tsm;`wx6wKF|)P4 znBx_gN@5+|UJT}+biaCWQ&9cX=WqJTSSCRQl*T_!5t%iex@L(PCp=!?&4_+WMTEk8 zmbcef_FN`$5mS@F`ePT1;qN)G*sHP-|A39RW%aYZ38$NXg02-vG(_SAbWttcYH*%$ z%3+}8vyV@6KrlR>z3ESd+@)`kls)_HL=SAX^Yxd z*)@v~mcR5)IJ_cjkh#@G97n>01Qq#;DEa|^rGM>x7k;yAmlmlecjmR8CJ7#Sl9b|i zbdruF*A+i+hIOR2lfw341s;G6Iaex{Fv0!9#WYsLe7|nUHrzk9?u}-x}3YF7_=$SL{8HDOG@3Slw6?+l_*Ue@3S7gtVMZDnE}6y`v!2`PP6Wd|Ci%*EerT<~#b z3vL&y#fC`a;Qzv`h%qM&2FnnR{T^cB()#m^4o1>C!AMbJqP!zGBXcgvN9{Dg0d2ry zd>MAhZIS5>1_T8wyMXZw`6=poH21LGh?QYrU??U-8~${ANg;u@y_kYt)?jN?sfyPb zW|A@8(_XyDvd9i$&dc-awp`I2blh;?3~A%o4Z8{P0RFj`fPdZMRWVuzD3c=hm`IUS5k3FH>)qIgjzcAQ58?gslWmmo?|(5M`*=WJHUX*fFZ3P= z*W{6&m?xZyP$S_w%7!yJ)=j4vpJjC3;_@%=+6_}Ij@Z>R_fsH;lBLR}b8_@XBdJ@K z|2dDGJ!x%bsynU-5Nc3l$BZ#DR`3oSyScBr#C8wl)i5&xCHu6z4NzNbz;`hGc9fc8 z(6C$|J?kI9tarE|?SHaOf^3g$j&Uhk{)0@AaUno51|QU$^eGw^cpTE#ra88lQrm_z z0O!H_nn~%SSYEykVYyG#Hs*AentmBOZOlh#RU2Vu8I>(bcUWda^@^tWObS(v=V10ug>WtiPUPZ@U(s7 zXiDTyuao#ntqi|6&^T%@%eFqHoqjYbj_n2+_>^L|@UUZjiY{j_ik$p{I3X;tChHGQ z^lZ_B(U)#{>zYhhM1HU}=qVb%X?FW3)%5?HYFG@P4C0a?kJRUcqA_8LB69?;n~Frc zq%{(ASJfvdYkZ2WHf%lO!EumBr4pjX(IFAhGIH@KMQ2GFYkh{}b(5t)Ti7Oa|@ zSIn~D@GFuX&F!==w(DG!lV~vPtm5CwX^ud2h!K|Rvw~mL2(mhzqs<(5Y z8lmD=4=A=0X~x3UIbW>_YO7mHOU$_hdJ{5Su93>!-DDe$Mi_jFaR>_9RP5rtDIN)b zPwA0q3;#p+>}T?GdaaXP4(%0e?4@j%?;@y4p2N4LS)!`jKvkO0d%Q3xGr5e`Av1D@ z4WS$GbZG=qgvxRY>50TK9a_Kj zMVyLpyjuS-XAHTCIE}a@Px^g*AK*#Iq!xnJB5*S?s{JL~K_thYTkfMDPwTx{aHcHC zb4NNrm!h~u{iUsTy4$SK82HoU0|H2+F{szu#Z&Y3km{u>8$$B#%i4nfMzHm6`{b4H zs$9=A^=3M*H1^DmUM(W=i?=YP)aCx6Xl*vbO1t4jH;0F}($19|if2;ZGyuDWHu2;7 z{)tD~_HBMoS=+Viw7w`1;o1d$%W=)J8A#bb201nrbE+IaqpJDWhV>p)jMI=qgjnwd ze{xQb+R?hC9Ta`I$qOv>iuBcbys7a6j~nj6(?DcK{jbQBFY3Q(y!t0H$Nv+VI)qj} z&iECsfL#>D{ok9cuR~l?Ji_4wbMLU^JU zwpKQLfBaxVU(vA^UUvXK!KijX%LV*-o8MG!A7{AXqrmW4wl6;5fS zbo@O?gfFO)vod>{7`56EQd)IQhv)d7Zi_Q_{K(?Oe*q7{4RL(;e)@3EU5)|`l1oj@ z46BUd12q9ex0MwdTX}n*WIwXg1xRYVE7P9#?z*1cTB@4O;pNe|yQuMD)<%NbHfRbrM!BB-7GZVPygGn4nT+ zF?j(V_0rvjpG$M+jNWb~qc=PypCj@uMZB!a@y(~BI1+6`_x3K{S9tQh zN7kh2$$5ak++xnLUN3m`k;NVz=!Nf6d-RI*(Uw1Y6f<8w-CD6X6=LsSbhg3cM_VC? z&m8dWEy-F1SoCLn#iiK#MZ;xM2JP9s<^Dqq`g8Qu@nTn<)}JvSHd3E(g3nyZON8Gg zYD5$K)eGR5(FF`zL^QZFnZ^_Fol89W`M4B&&|JDlPl@p9j<*>6Aod<~IDlxFFeT=2 zC^aa=$UCzTF`hiyx&G5%d+>ulKw!EQvu<%tQF~#eo+qS|HPq#fs7v_Q|%>()lK^OShFv8oplMkDykTAS6cnlL0OH!nC-1uBT>f?X($XgH@%Rmn*lpoEJT5ZA7rn8a{G=KY5rBA9H(3 zAtigtZug0%C83`dL4mn9yL6PoXI#YQaN2~4fNTvl$>PW>O`ty~%<|zGJG*5On4$2q{Cs-=SNB%J7$M->HWZs?_!!(n|)<4^(+MC-rCfXCxb?h+cYHo$yF@ z`}oq#*!ywQBP?@q!*nE*Z3^&Iekx9yP<%(E?*5SZAsg5Al+8u6x zPD5J&IlBmXy*(sH6MKrKNU6=wm3UX7+dShKgXwa(MWeB^>5G7tD(b@bXs2RdcAT$i z!}aafE=h4Vn6EYLVMcnXRVSo!=6TDkMk7muzy0xNpCj;#~G zE}g=qXY0hg+GtB(>kjCRb7%DGo&joi=h4}Lzpy%GPW}6qg|5+)HiFDoF_^27 zy8hhh&M(eo-eqi2F#~m6)_|s$2`@DZWTCMC*3&Nzwf^SF(3HTcxUrb1%?PRJ)Zdg~ zWRNDg)RGu<&3tZCVAw+3WH3|HB$Mg6FMRTPt4nd^aoND<+aNQq_L{2Br<3}Ox>;_! zxP;_fI+KzC=vPX;mv5aI&&qyPM8Up9;bb^ZTIR0>OErGtKkCaBsJ@Dj@nMeNd92pe zHA`xmbr0&v+2rplhY~40jfMk9i~L{w+shlN9+Ht^R^B#jJ4pGGfNshs#6$ z8uvEnd*DqgPVKPMy!LeY%9G0W!*6aQyP@mXign+lIK(A?*ozs(If+%CwOjzb7%hMR z2T2#Fiaq>^`CN_+udC3tNSV=tCd{lW*CW3fL1{l)$Lseh;`$lpR(+Vw{6eiY2a9YO zlGh@f&1Nfd*P@FbxiRrfI$7xb2BQ~cPpGjNo2k@;w75dPL{w)U*0J?9@||e{v?!H& zS4lpTE9#?vqdZx=ig!Rq6c^tnb;Qp&#Z)f(y2+e| zPfOuAw?Rkq3nTTNgkaJ&oCgA^v8)^ZeYJg2p0$eq2OH?-MuHC9(N zebqiY{-5W?t)#5BbbI;tD<4p(pDjpEN&qgZm!S(sk~0GOKf_f3JxNM2)reIpgvcA$ zIo#5?XyzbTl4fK7Rsq_V(#rX7_IJWOa*t9wMbo=^mx1ne=aiQ8b2=1#($x93)N7T+ zb)?xLLivZ1%(oxAQCRj8_Lu2#>nW$jyuG~hBP|YKn?4j-Kyi+ULzB|R#T%BiB^$Z( zaDQq2%=D#|SFI7o2s(xn>GaYbW*7yoUBo3t9OI(i zCFyObt7qrL*JOhYs)+-A-*bQ*bioSn+##MgiMLXxPhE&+i~o{W-3hXQxH1I9RVux4 zk)dz8E`mHY%Q<6=Y|-zsMDivLXYY4EYlr2QGTSA#=tWAUHQ{;Cf^S- zb2>YY@OLu9u4D>EuR zq#G?vHVIO0<*4MxQs~jxUU<7P*0Iap3K4$}JAR=HRC5DKZxmbw8#f)O%mLsc0ZZR! zvXiAa+z033l`FD=?WHLkHX%3VIEv8=HLu@V zwS^StyPfw!7HlTozaR!H2nnmlpAOBx77t_|#vojqSkb_kM?>L$Yf^bG^Zvxk?**dl zn0u6XX4l}hZ0$Qfo%Tkm@)mw#G~eOLs^j+dpH7e>OSHkxS)zP*`)_;o;i^nd4XQeR zyMfK@7JKJQ=>d>;IJG#?K{tN=O$bAf%f}$arTsnQkr_V8t9}`xjI@ z**xEzNN;`a(Xv4aE(%@w8zK>2a9;#-C;vNcz>Q*d9<=P&P|Eq690hNhF{hqsX?**6 zFO~h4hS19llk+u${z~JJod{(CUBs62Lc&AZ+n9~9oWylu%-4e)v8Fr8M)p<}vl1`} zP@7)r{fG$_ zim!T%`46mVL6DB#k4`)|O+q>^rS{p4YR8}i`+iQh*iV%2LT{1%WYv|&^ntv~p19?d zO57F$G0MY|j3Wad1!2y7Woq}jNH+4bQ(g2HxL+iV!#i&?7v%FkeoA^d`H|~c(C5<; zdX61|2}gq(NPEud%Z(9g#s=v}Y~76sAAO{v?5fYxL`|?~#13zOh*-&`b#ZcEBHe(F z6hFgF*s!QvQ9R%_qW)z18C70d&?gpiJbG-H7)|hkg2Z^l6mFYd5DW!#pRX zSdgp3gAenzC0F*zp8Ram@ggVr6Mw5nICS@2rSN?MNhj&1LO2k^Hlcnn`=m^H^=((B zK<67mkP#`<2a|MSEKDWwoZC(S0@ITwt;14@HSV!Fy2SDeJ<$A3MF!e^UI@^yb>IWJ zTcDn095dcG$C$xL1}qdSU8N0B17&iO{%zlYzOiNLwfG9g8ADirV?pO<`R0eqfj~p) z4XQy9eoMEo-RwJ#rrt~~p^3}*j-lb>MfFZ?PvOqYcZxn?d|Y`~_}toeLgqH9z)jZ8 zZa1Y-O;-LPW*hGvB+m|U>}eoF{dSgBr1StVN+zl{L!8_8y!o;tVvbW`an_n=sp3dtDDlEDF7d1o0@0t-G-p8%IePUgNgTb>8S5It>o?Z z?k}d%0e(*SBG$?M8!WAyR@bTUtLaYqBS-ywnGCT$7o`?*j8%BA72q3mkq3l>1lQe| zaaWVz6;Yf`2~$CxH48$&oQpjjcCx9MB$=?m*XD z#qz;Jdo#Ns@uLW8EZWvQXA zECM_&-(haqk!hU{W8J6F_ru7rAEYAMN8+@hfRqO2vgtC3x#IcI#@lKg=>)glD%=A3 z(z?{zI9T3tb(8Gd;I)bb4YT!7&gc5U>-3B`sLv+Dckj)Dr0FXVU)-s?(<(vo@f}1I z59}r@Kgw-a%6E#2C$F9-HOcFKlH-Fc%P%_E!QQm?*Z2WR+PxAmud~s;C_X6WMvOcd zXlh^%meN~L_Hlkou?Et_`gE1(s5(La(C4WU1!Vq8Hd#Ldg6>r2)hWIQ0SOhV7T8i_ zIT}R0sJsOVSU-RMN-_(!)lgzmp-6Kb6F*Lt+PQqU?T3JfoHCvXeOxU|-$mUr) zUAp~tW3&DJ zIt1dHZ6m2Mk4`pr!b5?k_|zlqTMUA3!*t@lI_2#wa)~{HgqA1eXdfJ$ zfo59GxrQ{}`B`i7GR1ZqTsby)O5yMsLasbj^wtmUlHNMKbeHEie(@G0zSS_NvM7J} zS@zJ}QE3A>^XcWPqdsr=tn0=!NdW%9P{O1~nE{KtmshDg=l;=}vIs8XbUG~)RvM>Um&6>OSXq8}^9IZbzck2NE=uq6wcPQZ3>$|Fn@s=|rPu&ye z%4QMxQw{_h$t$l`F7?y+h|kPZJBhsj%PS&5zDaqUa5V?s0-)_ozwV?jWguPoqdF#} z^(`Ov0BqIy0B9Trvt%3NLQMO_P=9gnds#!63KU~JjBWZ+#rAyv{DWbDHArGOpnLya zt#d%&@j!K~v1;A~Y`OX;h-_Q!E7;3MrhGO$pCM-`BD= z>Aa28ZgC&p4PD=0q#Ik3lIacduC`vn?vqM48V4=SDqjdkViB({&Y|yslKCx zg?mY>fD|=;GG>)Q_dsV1!S_L}{1kVJmFxa8UQ(Z$DfhCv6%^nm@s=t4z`G(06e^`B zC5pw*^0@Ii{&<-m^{u;`74qrJ3%C0ZT%@I*bc@e_!J)W)pL)xDtXj{3R0-esyiKuG zK>*j1!1BT7w}r8LyK6u?F9|yIC2q4BfXb=emi#Dn%IfMHTb4|y!o|CgzKsjp%nz{r zcM)J|{oZf*;~q5{yX=bnaSmbNgMC(~q72&bu=MbUV#^Jwt1C8>cO;LeZB`19_y+ekN2^%3ZXElX|jO-}KwjIs4nu`K)GB+LR$sSog>GsA&!r zIa7r7@q|SM1G*indrJ>-Xop~A9Dw=v(`q?E{xLg#&{I(tOm4Uy{JXnA^QlPqNK4@0 zqLN=Z&b@3w)eHAlgK?hA$I}ChV5_F9V^NC;xyj7{Rr$6WtSyiFHPjnYi2sZ+rD_iW zj&iSP(^m!zHa_mGLJ!m$8PS#?vO1%OqaOLxJi*G#cKral%eaA@ZxK{n=!+Sc58YP< zLdSY<2kgAmLdcs@7gr}y?)H)2?buDa_sta&2)4e2S7-6c-5Mv&cuf9@doN=&dZ5a3 zd{4s;B14Xn7{ajtZZ^iad=%33lapp4qYS9A^=H zG%9GH{z8U%NA!CcG+O91$m>l_Moq<}GUeKUwwv-5DoeZtE#7@yaOb=J0}GSzPLj(z z=|%4#C;1uXlhROMueGEl7sGn98Ms`5gUz0ms)MbzPG(_#x~y<_Z%ifYvc__TmmSjV zfc`ms_T5+%d_W>=Dsz%7wdcCI@#0o&k{m-w0AfQSeuj`)HS&lI6X0@R;%ka&bijYhX1sH4Yv-r~TIiu!Hl91`msw zm=rJh%N>Mk*GB%D?wXKW8K-czk;+Mfk>GSbn5NVJD(bZ{4D+N~QidY;(QMqvPLpjH z$kyMVO@8Jg&+%|&G+hGcs$wv`zmX~?(U_&41s!xBQxjd`Nh}A1Whkc{Ny?I)qzV9d zeiDV*vd`$QuDR%9VXi)t;WQfz)~)Bh1|X(Oe05b!4GWR1d3$IkWP><%9`tt=h|we1 zZ^k(0E|)|nl*-P-Bvg{sjx%OK%ZZQ*N5_OJpwG5>6#L8iE>+d&b2(ni#K1}^DK^$V zfTHc^Ec(|-WPW}17hkBivN2kJb9|9et<;r}o_aU#X7g zWR^NVaBAXr^>J6S-3}^DauXM(L?>h_xz(z%sLDkWs3x2J91R`xG{|8Nb_D&siF4Tz7Vl;5O04h6U(KPdgq=FK>BB{-}4qEZX>j!&{Nr0-z$U=t8Ze)?C@) z>m@Dd=nn|#APTn5I-=;Rvss;j zW2vTxH&TqKDr=flsAe72wVz5i2kTOSA7T|~W*VYrYkRO_9?9OJKiNPc?=g$!&A6_w z>`e#-8f|)dQ=C};ynSWmHYOyJ#F5={hd^@sUMdaHG{@zVJB-+|#}@lGI-$-Xu$#Fy zD!ed*tC-bR1$fDRI2j0Jr}SoHUo{uo0_8aROa zY{2W%B!7Pim@V+@%a;MhEJ05p5mE(@{^Og#uUJ^XY|HwQoE|l3%>uLr~BD z>#GdjbOF11uam2<0Hy|GD*hHS?e?AXl%+5Zo5^ZBkVRd2uATo;U(Iq4XES%RQii$r z@v6C&u9GkvrXAzRO+>4B0|I1W|)`WPa6OAjv)Be$yqdB9#8>+4~Ht}37?%l?p^IE$jk1(ij;hhD=>gHo3cQM11mJJ_rkqahY{R58&5|FkHu)c) zood9obYsau=zH*;;YWxVBCx=UzT@j63CmyV=6j_Ey%@AnilIh~R2Om;ZTm7)Jab=} zKEHzGsos?#+N@?!N!?6>qXs1WAP>u*H;+^wEdk&}PR~AO)gG!VLWEtGQ?VqK595`5 zKS%(m&Di-P^@=NlUrjL%e&!o}MyskC(;21m-lbJX4kZIUK6?P{iwpbIpaPO4y)obG zcJvcDqGa7~&B7a%CQ!{{E&GR8nz&sf{ii{H-nKqN;O^Fk{Ox+C)=81yThl;!{+cOO z)brHM0w3qqx%M8}53d)O+Tvd?NEF&z$LC+Z;h=O0#)%T~Fzo$6e+YYlj@g&jiNZGzIJug`p{D=Wo5(u4mKrwC9}OB<;xSeO_e#FUuDsKT=n zRofOShy8`4ZuN`CNkfd}*_1wbG|Ik?M4KGw1hYs0H4VcrMF9mreN4y)*87V_Q`H<2 z*L?6sA3H@T7et-x*SW@zq0-DdYjpSF?z%DO3su;8e*f&nU8*rXO`Y2>b&eN%zn zEFe4FG(%rwS7}5nt-5y_{mJVFAKkFwo|pe-j@cy~P+?Vi5M?ZGPQ8>*Mf}Ku;O*@qY z8Q^NLF9hG(OkeGOrem5x0FVH-K)UtVq#w11j`d}}oZ%r38_dsjXbwES68NdgVsCRrAALCq7qcP;E{`iH#7L?ZQ^!8Cb zH`rEB@2GrEdw^zon&-S&C%TY&|L&h0JGBwu|E^enWAS`0fn2x>WaX=e{SrG5L}O2% zQ0i{I6_`G)u^Ko{;FW_Eg7OismmN+dY*7c7*HimIN0}VTmj(uBJc+wH)ktrb3OK$V z)L>*saRFqN&XUMG@Ni4l&aNiPz~SO>!@zB;WErUOabHlPcK;z^-^c~2w;geWcT}Ha zM|P%(%#ZUDyYh$qN2W6U8C+*pp85whwlf$X%3aGBIdJ=9&W{|Ukz6K$t76$?T1DXeRGj5t};b?^ni zW;h_FfLo7f*+>sC#0eQAXe~8)l<70)zpX=DZr~4?i%Os%X1iY#D<~V9iUmvMEQwG35>+uWTc$1#N^N^OK@BvK^>SEJe;B-sZKIOD5@`cgXy(dNv8T`G^ zE_O`#Am<<3`(4>9@Fk2nfvoN3YHMu7)rDV7EuDODnvrAwPkSv;FmtnCW0dNh^5zSa z4kEQj^~8ai4f<(TFzc^7dwKUpb^6act5$EUke~moo!|4cU)0{LdfK{zA?vGjDssDF z08gtl%0S|r3hwn)#Vv^0Ed_+AtF@dqRi)aV>8%+sgh8SN^%3m3tq6WhAL#9=s=TRZ9DBr3ug@VimdEZp6|6(8*zlTp>AHOmy_9KdU|nVzZgH|Bg*`94I1t2Q_Mz zC=DGEKasTV1YZ_T_#w9XD2mJG$vm=5yZo#>^<641z}v4GYXwoEUAYgFC;Z&jh9}q; zt^q-C&7&%IlwO?acg|HY5~ovrVz> z)6_bmx8rM^c{ZRZK$qsAB6+sz?LcrypP&n&_V1;)H|lUcT)AqGk$hN%-y=W=GfGnZ zlTw&eZvxDC|g>`Qd?*)vnc25chEv1d5kv1*7A2IsJ}~JeKPz9XuQs zi9!idMjE%j!#Nxcj`@tw!`tzB??&vZW4A zG|}&THwmjBR4^54JU>S+&tRa|lBoF{nJF=;6R{2Wl1A$Qq`2s>$a#alnQG)*H!D8> zfvw{q0B)56L!peowem^v{|_a)jH!b~>C#`d=7&mz+6YB3?M)QhBwmF*AD5U55T8LSNj7P>uf-p=Q`n z*9fEN>&_2Nm-Qo*$9%Ol5pB#y)st^b4=xOL}?WzI>Fw`VA>#VW$OvBl7!+mY?yKkDl zcNG%uZ1TPv74Cl{)St?ar1J0fLX}V5+S~v9rx74^Ky*Wl5C1`ogzuhB1Tkko@0~Pl zI1o^{P2)rc8t8R4u45P48?s;<=rH@04zw+7gZ|QJvaK;O>L>09ZvoVrIHmTMT>z0)s5!a zu3XO%V^jLIXu@H~K-tX9EW@kl$yR~i<=Q|^%83NI4e?7n$ps-@C9Kdt)D$URVqo?h z6n~q&teWxq$lCIXe0*=v`rQ{LLG^AMlg7K;UfrDL!=ITs3SK))HCF~IX8@<(-sdb( zsjkS}xMzR7DGc=}%XHHfHT#4>L6uUhV|pNl?&5f*nHob**J4g_Dcq*eHPbFvb?r8N z1@&m~>^iDoF!o>SlJ?D6yZ`O137H^L^~OcUlom37*-ig*y0_7>8Kk&0xfBFK{v;aq z=Qjhz{&OHeKDRPW5dNu`RubM^FX_LVKiS4;*iaR*E#I0(qA+T6K#IAxR`?hD%Kj(|1S=UTWFy-{){J} z){$7$5Y>Nw;)bd4{Y@SIyTj!F*KI)-7#Bf1AzCH5>}s z5I}Q({ud__z^m8*E)w{6zVo+%{`4`?DwXhk_UBtc6zOJZau#soY-UmDy~_BDPyR(+ zH*81ENa_<^)i?ht`{{0taml%Ym*}96+SSGiw>30J(a02Ji(mb>nf`btHK$=_W<#b1 zSmwqlL51t<6uh(V)QSJpKK^x=0qz$F#i@KH#BV!Zp4uKQ+-JwGcTxr2DfP0YH{jAV zlVPq($o>1_{>g(eiB$k5{Ov<9v#x@y)PLEEK(rg*RjdhD9(Ueye{pnDcEn$={%trl zOn^_n?A4_IU$ybojflYjsH5ia17_rn+{vkW{s<+2CQ(ks z+#YyL`Co`uiXKpTA1Tu+;XNEju<00Mr2N-)J$VEi;Ma3w>xsMzay+ogv%j6+pT;2J z%Nwo5`KWb-NAW0p`M=T%(`P^+eU|^XP5h5x{+EURMdbfic*VXnHuQqqovbt|hHs+}?Le&(-4yq&$&_llttM06e&bxhQ3ePp;hEdm=4)1hhgGYSH^I5?kHo2>()H zev@aQFFLV$Uphbhu7QUqFnyDkRUV&g?>fG0bnqz$&HyDKL9iQ7@*e z6@`!m8T@Iw{kh#(Sf`FfZTJzTGu-kq!u)0RoxKn}JM+FWfrBDzrO_Z+)64npu@WU@ zw-(ei4ZA`Ih7GUI)s)t*+)D$z;l03BjeVe%Kag&+Jn57-;#a1)(*IQKfqT?HnMiCx zOXV8epr*;)y`iu_gH-XYAogd5zH|pDQZ3?*k_Dy8(dr144UGlR2BV(x$ZC?iS@lwj^#3a-TeF)yiXax|BfetuBC z3a=fqnsr=_qcWlUsyp%95-~u38NatoGyjsOLql7evVJdd6|G$FALN$(9<52#NLDs< zt=hAOVdu4}LR6Mm{!irV1OcvcpNqH;!m?Z>(`LU(q$nP$#KV8Qh1uwH!ZLLKKSsJ0 z_y+s(5Iz7+^z+e=u@_HmK+;0{OtTMOuvkkSf(<4GoJ*jbKxF3OjVOFtq>|1zw3T99 z^qeeU`k!$QSyDBwyx|JD0fw!VG4->9#N5%F)inOG8Z01MHemmcXieY)EMgYOo0fx1 zPGjbK(u{yg9F?V+YURsj<{;I)|F zN1q5RYWF`*is_?NPEesLIMX1P*YY8Zda_vNl`3#^<{5rd;8O_8eKFWS<+cJ4uF-sS zYFTr$`Cp6CK^>&3&5}Wdha5`(d{DOtU^ZU*uYnmGVch%(2!%01SYkF>Uj0pI0&bKJ zK#5~Xay`BxR5Ck}_vL8SnEx)k1@Hhy22$yNNF&`3zlT)#-y#3+?qygKsZ!(KPjr`d z$ywFHroBD%6TiNt^V$LacZUM!P0NFm-qx)T{OKVqdv}fhgZq92l$8kFOcM`u5YYF5 zzb+3QgpfQp%#iB7IFste&;Zv<5X^!}T)yKsV=*RfineO}T%F+lC+$A~6sNdy1kx%J z2Z}n}g)JXB5%tBoYMO_mu6nbdDW;g+Bj71Unaje9G>aic zuSYHVQtj;PmsC7@i%Y5_COq4ZQb+e($}tlnTSRxjmYM!@&D)On;|{77r}G9qOM?_* z(V{!`14@}Z9qctdD#+BlB#OabB6&K$&M6%PX?=zIx6XgvIT5(7@%H)K{Tc zG=oKZT>{_S1D$K1VbGlCdu^X3SI}$?(g*jM_)pCO3I@l9^OBg$dga>kmpL++jJ^lp zV{Y;vFG(jzHq{G3XHd?3!+;X|nX%UrsCc9EByDa=q843DH+MoiNH^Xl>Es$sE_ebN z>Ae-#UO)RpFFp_{0l`%`hqgVD|EW_@o#kR;VY@C6SnWeyY=10Dh9{darkjzn>HNaC z8lC;}7GM1BY!KDV=7h_Xq=Y5V1MP%wx}M!Ic8lTbNB!-NC&xVkfAD^$M1VyZoLyv6 zZ!l~&O<=oOJki(t9L!u5pl;O*c@^o+Rdqh+AzGo|{cJ&^dvluBO ztYgnE*00W~m zef{f`o3!UuCZ)^7U1)CZqb;u_=lNiQiQS#L( zdZOFqi4)F|%Glqgp9sYDG(x_ECm8;?$aw$@FJf(8nINdgI{7eOwf!N)CeoEFQT0c{ zfXGCtiHkM&lAa!eUg%3 zTbfd~@9a&1+NB9V?T~F%*^eREWfHT09t&q+5`|LX68!3TAR^d1Wm-^g#&7wNDpz8p zdXm{b)`y5Hm85ooK4yru?T^m~9AhFAA6!zjzZfe_PZEz6;7T4mhv%!7RfY)yp*|O= z!I%eYop-P7w$UD(L%)*<3)+1;&o&?Sf-CV7k83PC$73tY!A+FnEld$?)ddIr9OLO_ zA8bQCf>@i7CBT21YL>6i*!dwM*GXi3az?00A`3wzKWFl`7dl9}p)rz#XQ?8~Hh)Y% zh<8mlt*q;MBbX9OF-pwnX>IZ*wXR1Us4=dVnPF6G+KtMHaqrE`7J(! zns!AWC22bzjUjhZPG)Frz~^4MY0W@PvzlZ~sv?sbE$eQk0^Z+=sl8;o%^nY+b`ybF3^Dv27YLVI;mHd|VgG$i9u z9k=7QkJMll*>Y*{E5AmCVcE0g&NO=0w=QDv4c=8=Da0^!qs_1_bMX*uit6N^bdAST zLU3eUaSVgO=M!YheKj+@=LFSCA`?Dp8}{WW5p$_`Ut`3PS7F2|Krq9;ES3sblmlzpQE_Y(TO+IG0ajkR> z_G_D7I=m|Mm$cy99y;BdC7)Jg>0#p?tXsVRdU2YMC~$MQEcfzr0^d%mUqMzDx}N-6 zcUQXxqJV`%C%@2b&cDYG)K52{Iz_iZNPWA}#OagnZiR}0nPye$7gLE2d-eufJwy_` z3!8p9$&8jC*rstPC&+LMd>mz#TrT!QuN(K@U8JP)`&3_~7&-q$nFr67nvSGa5H3OW zws~&)yCO%zyJ$_e&o=sD^OCIzXw(R#Ud?!v7)f4jDtZ)&d%G%Lsl0Y>d0<|lELb3y z?Xwk{jIw9~+5=wr_{C9!zjWfcmpPSV%+mB z@mX!XfGC#L_V;yL>>dC{H!SRRs6$EwO`EEuo6cldmC}Z#_i1+75htxnMv7!}(F_aF z+ZCf???tm4f!{Uw{LK4+-vnt_&4sjY^qTZiu2Y%JK3=00f^Qk8l?d=6?Ji*66>mKN zpyJO#f!;XdSY4Agu&F__^*(h^M z(lqAmb*AmO9uSBabCPLw;7FeBTtnop&osn@H4y^tlTMV#ts-S>T^XbP=G|`^bcyy8 zKwGF`+o~m~n>lkfYbo+p_ zP88dW$MxgoGqUUCJt~s-nOvK@A_r~*lE>HD*&86kcI%BFdz zv;bo`h1a4^^8gEK4_lx)=uE3Bk-tQ_iCOdK41V`;(@+cmsY-Nm#=$L43D;vlCIPx= zg-@eqJ}*V;o;I9mZS_3xwIQCJ?Bx7p{0RI>7gPqKx-8859&3g{>2`#RF^FjQ8vmx) z<@(dizley9S?nX6pt>Vb(%o*S?LejGwiSjy#kQf4|97)O?tVTQj^KOjOG+bez9*M} zEodEUu8(dbR8>73ewcr=yIM4G69ZFK0l5kd*UlHSR}9nKhczX)M~7K(O%b{-{4OnE zA83-zYR|O451rYs9c8z|t-+>7Js^<4)yWH31MDrJbeeQ>*CQHPV!L&V zbyxoRGY!8D>WF?L_B`LAv(**D67#mN*~B6&zP&Y;w3Er*%V~C&?qAtsU8nnnU;?)A zfuXu%4-j-e+-}LG%I{SOXFW#p*M+^G+Ux!pHO57hZ;YKG78PpIPHJm3R9giT$i zuX#9MxNr5=92*{)PX?k|O;+2~>c5`6$++|RuYe|*Woc_d{o3@&X06Ed(Xavb>^?KX zF~XQ7C?moTKodBMJ&7(!N8%`|H(d@w5p#Ih~f2 zO{cN*K`yE`BS@U~FtsgHKjc$~0daTt+wc1?03cfv)WyCH_=KYcg!7p87IOm#@#MtY z4K^ZTgCu=^aXA@OK!{kYKp{M5(WNGSoqqnX>L)$l=gF zK)tlgd~2{CJIHqkxq6sglDqvZjOJTe)>0cft^VrmllSfTj_e>;T$VXc;Z*;%0vFK6 zdd~*X@K?`zdIM^e5lnCy{MsvTNj)&CUa*iqvAL$wm`cftEw`uZa>7o=pZd7#YEvM| z1M%WW)s_rFX{$(aolPV0)&|V+6qkahF;R8;2S>wc#E1-Pr+R$c)&b}UE7-j@14DHp z4aj)6TslbJr9R$@#J(JO#nF{x=IlkJ>)Tw^lU0AdMp5v+D>uop-l+XSwT`-!^M|WC z{C17Tk17sO(5sj3Ia~Id{?PWV#r><{o-*aSkcBk^b3L_lIu3mQwykxG*_wnY(;8|6 z91eeib$z)HRPpCm&c`W?kS0HG*l3#bPFUyO*QJY!f?#Za^P8C(XpN>0T1>k`$;_8*%mTt;nC;+1 z3JdUYPSR=Qzb^?qU{y`4;rLz5286WOPS-p|5e5 z-cWmaw-js&tBX3*TMZvE6x>K_D+G2V2rg6>K$YM@-7&S@W{-}g8fDeYy0*4h`~7Re zFG`z7WP34;ip#3M2`^^*sUKV>JN|M}wz1{P!%(^_W9^j9CAHBdTT(bur0^79aNcae z`;@Rq@NuT+)O|P7p*y+tkIr^YW2QZLC?}jM+H$q9bYI9K<`;=#6#DGf-G+6H9&^wv zROkOvUm+u#R`mXX&=Iu`T)A^U>gS@~0uvj^l9bG#J(^mi_9*GAL4@zw`G&)^ry=cx zSJ{vYnLU631?j1-SEG73^1T4~ShU zT4bJ%?9{TZ9B8YoUC2>@tv&Llg!^f-@wy?UO4IHzt9?bvy-lr`&i!YfK+}CZZZW;? zwsj%5=dOyhxUhXLkB*V}{>#K#MHlIubB#q@8@=!t!Res#VNNpCl*8Av7v>g7&2L|2 zbM_{laZyx%z}8bYJD>h^mEpMWXhD$`CP^A9j;*6zT@ua50dY7&B=|`3;RhE&F9D!M z7cA(Z&$6cI^0faJ#P*JDah>b*Teq?^Qj5JEIFMr*kk5_VPO2$_A9d!@`TB8saS|3R z!n#R;HpXRDWl!*(a)|cpyIs7X=#||5gMb&+Y zhx44Fzv$3qL;_*9hZO~PLG`pv73|U5{=e=oV0@3-xqroaR_czM#J_b%`*QbT^^=zf zzg`DcTeoi;{m9XWZv}CoBP@@m?-bQ@6>0`42xZ3VqGfn?3m1k_RzY4N;#_5?)knl- zpMql*YJ5%eLkWD-CR!Ujrp;jKg7+mtpg0+oIb2h)-3GXlATp1EL*o4qKc(>emyu1V zVgAo}f2Y7O0jAlHkNf-Ukootht~X|BUAy8+mz37&4lefxx~?-QYDICQM+_`So@&EN zI-eOd6wj?hsYHDwz6YA~5_H`}ofo(bKzcYnczmqNROHGVd=(f?GtXK7>x0HdqG!89 z!&xb1yds>R?iz3|+~M9CUU-sayqaclP8uO&m*v2X%Rn>_ZXx|iHWP)})(#H;t&r3_cTc4#UT6H(l zQiQc?c&Z8TVS^%!{E>JMwqP|OHU)|Abd66r*i!S8Fu}GoQC9JVmQw}X+6ecf2$>u% z$T?i|G2C9-K#o|duz)hLKC~d@+Pciit-sQA;w_6wan30@Pf5Jp`G*wVK2;)yRQY&D z7&mC9+=2IC;dRM1vI+nR+K#h35;Nj@&ymA#{02J25PB>_?CSU)u4dzVmu_bhj>OQ5F2=Xat-go^NDkx#jLLhdidXTk(+As$;c{~(^&@f=*C5bXy zf~j}1*YK1qoj6;c*mYVzGn)MYYbsW9*3$ z!&9;cOi#3RwiU^h*32-ynWgI=;Qyd;|9aT7?96#?cJ}5Vy5JZj{*m#9G|#!h*?QL{52 zXC=A@?FtUQFlNzHjNdg{X*X6V;T~a;$;%m|Hz_Xm1(GrILA6J5Tf--7`C0jGv92rQ z3C1j7%p;_O0+;6d4dNL2? z%G-w0%|uCiq)3)NUN`G|Fhf^QSnCK#RnmgS>rftuOxmu_^l2Pc^N2qw4r$d8>VE}w zgDXpd$4#terBdu)E88$Zu)ALMj3n|*RqNjVsr*b$Y+I{VzkUbc!xPf+j%g;@ME5`l z)Y;g`sr1}Fo?r8FcO<;pvb%ByQO&yg)ZLevWIBJX{;WRBN|dWsL98<+&GI^;>tr8( zR5)vKK3&m2Y4bU##t{q{HUbL(?-gKx#LY&Iy_hy==RR*cfS=d`*}%9{Zx;?7%y93R z1QqHeirg5!ExAlL7tq9pIaD1xzfGEp#Cmb>3UEHBAUdxsqpM4@DBx&C+^~IzBR!w9 zrsZS!Mss)(H*&v6pvL&dp~_{0zqLTn+&bW^t90g6JnXh-;~3_}p@t8zhShZn*Diw- z=)Jba%eVv3^wOn_ZLP1&?1G>%|5FGy472EN`*w4gq(q`*W;u?MX>cLS#aM+T(w?zN z_QNdnTH{uB81HqGP!+>u558fh&Cg@P^_rms<03+!29n#PZD2XaUu+vq#9x1IWmplf ze7Xexo9U)2E+cHm8?T`g>*5%>v(X1fH;@$n{;s5quR3|g8k*#^7NI-TnTh%LQ5Lklmb~YILn&S`E^*rxz4CcA8O1(<*MJaq+42h7?ci+wNCS|FnGzYqFySS zvu^fjK)G>*PG7a8dW1#Al=F<#d(JoX3xy;nRm=3iz{-rn524wt4*lT5a-7eS1lNTZ z3J$i1>^A%bXJ;$v>lTQIm+8hV9c^TPc=IWU0kKFNoF(XKRlzY>q?&b`f1p9O`2Q@8e=-C5!YiVljxYju_=;KNDM zhg^l@-Ar)l$0wC>ihM^mD!NN zmB$BXE606K59^7n!PU7n!G%X3IIaCE;{0I9VMYA%mEq~C&cU?3kwlfeDIjK|&*;B> zt`R8`C4U@lCwx5ff7(0qc&OL6k5{4^q0^GS+A)%S$Cl09hY=wEC26Ii_G^J-R}0TU%%x63&)T=zW;B+y*wF zmBDBv2f4((Dpdvp$((Z?N+-+*O|Q52F#Oj3K*we#J*qPR$)8MN|1E}Odv;DP+>Q3& z=Q}S!m(>TY;Ef6N>&Dl?(o*42PaGj%S&E$%RSceBfs?1%?YReptB;HP@ho+&$uHBX`C!e zorcMcL5y#0iY?KtvPeC&d^b$FU^;&@bSTg_w%)=if2_iJg-&Y)8be3>qBUe6)&%ai z?W9T8*#$J7jfvKz+3Ut5FQ9Mu)q~)@CWz8@Gnq=N`Xuoe(rs~^t!~z>hf%zbW?UGV zrx^lfxP2EwpGXH7^t`t)|1B{EmhZE2f+M9G6S zH2s1CO6@?-9ii3t?QIJ&e4@p8`g?M;^if~&W9DLm8BGUFC_hCv5ipR>#djkSpi0_3 z=OsWuh?UxktFrwHU^7R0KtYas_4u^bjxeY6`7j}IpZDg1YC{$}x56#H;NEe-oZ{y{ zy|<8Z?glvvq$9s#?6TzTSC+0fJaheXY_JD;yF|EPLYNzN2XJ2)W*165IM9k9m^BX0 zpXsjYmO9o89bDS2!tM+dv>raKq%h%Il|R+E7h5l7HE*d*5`jTU^`(-C{kaKVZK_R} ziuDa&qdfMt7P*@@{mwy}-Ks|O;Z7}T&U-72UmYM9B+U*|t7|0HI7E!OBXjC7a=db7cy+*G|L9YkpQaf8rG?WvlymNM@9O}ZUq!Ftc?{7(bROA} zdGm#>xSwUzpiG96!Ci^rr@p-glYgM0j)@zqp01|IG?;hpgS%Z{#Ksc5p)wMAWv?fx zrQQkJaR>DswFDz-#Fvc@bNxJgjU4i2#`7|k>x`STjN;6*Hv=5k2|FHv2%|JJ*GrrvEguuoW1C&1yiRhG-TV{EQOpRg0DLMLxp|!V4b)JlMIoLbQJ|_N5 z%U$eL>s|uk%MXCiV_g#Yo}^-HY2@&{%!9Fykp zx2BsCya$>%5%Y3=Q&0?X#rFi|!%3Jql%{8H4RgwudW8s#%|H7RHP$CMdwhhW9Y~8A zBDLpeK5Nywp|vAh)I3BveS9EljPSlYg@@l>_#(Q*3tE#&9UV zLPx(S-{6%<+FZOJ-9InBrA$mcwBPlxzRP;0L0dT0Eu#e)V(}_F5J#K{4bF7=DBya= z_0$nw)Xm@X{rc$I-Y@?$;%jjKbZ-&}AXjLcGuOTG#rUX&m`Gcq-pQ{=c(2p7aGK#i zcSmWtUaO1hAk`*FQ(V2mKvCHH((L%GFhW#rsrqon;W{!IM{2uqK5Je(EhK@kJl zlVz485r*t)^tuH&C!sEkR?Qu5Wv1M4Cg0fS#no27c|bVmQkAdp6^V}u=qoNO2Li0& z9nQ(>`jTyd`<1*Xc!w(M;VJ>@ze@98QroX+Y96cTM3~)mkq9hV%cKjM6?_dn?(>h= z@lHakr`3ia*GQn=vwSA0Sdw+jSJ%cGlWy?yMpQd(^l9ICkGAh3G$-y8KZ5( zf|*Uctz>^XL4_{sGo7ldWsPj`pH4LSkpAbaYCUqQt+|NpPbgO11`aYz1^K%MHv=pS53TSIQRNcsAd`~mHOay8pehMRlph?SK(5-gt8UhB zPCXL_t7CE?-Xj6JSs&hs?B)^ET+b`{ykI}ySlQcSl+?%g!h+}E%X0ec&L1Lc5XB+z z^ZmAJN(ag{tN7G~#`7c4BQZ@y7j0|RV2pj1`zG`JEAly9%yUF%8kU1aQ8OK4_(Imf zJ+L@$ip`u-LDhymLY5|KUFjtYap>OoOTx`q_;+Ay~^E_tEoBQR+=ttIS&3;>-29 z)`E3|3>H~yM)u0F&lb1WI1jO{P@_q(y6B{1=xjw|(Mq2E@Y|1wC&+fZ5@jFj^RnhD zsMymNgf>Guhi9043C~!sOzI%Uu_*+X|YQd(KCT10NYCbR0R=bW1N{k)ZW$8r=Uc}Xrp@51>T;GW$)0}|4^4W)>t z>7eC2mjh+hE{#!6^R=)NKf9*Fu2ycp1L$lK2ZWe#K=wo<9|A`KABBE%^MmbVIxbB^j|aO@ zEwCH5P8=;vm z?IJzc)yREQV=QwTmM2G+v$a&&K`(CI57s*)|9+0b0c6G3%Omf%bng}Zk>#P|lcK-y zBkuU%{jqhO4d76*R=DR7Dxw=?FQ}d#>@GRsPrNV`Tk2)q?^c)vraie-ev`D68 zUHize9;NZuNduPa3VTb@M*Fn_`hU%0QPrJKULPA2^-_R>7j)}c6o=z{EsG#GUr3g7@5}rUC zthg~5AXXc0@dO~m@!vWRc)3vHZdUvB+Z}T@1Mk+wscC}5pf%olzKavqAB2J)63F7e zW%Yp9lI05X7JdIqQmU_&WGZ=s=)KQil?%%Aj2&?$DT|_O$RHz!KC)j=267pQL0*tg z$uMs~S>#*xZx6Bt?B4{Q9!3d6uqCijPXFSLS>3dgE@Gu;jVgjR3WDjK@ zQo$@V=b>5bK4^*IYlXfNH%iAj{L+p$*E6r7ygHJECj5$7@- zA?#Qm07^N^lRt%Q0Aa+cRf639GtPY5@z)A?|2(2~y~?$J!E`+yFHK@QQC{595e7#TAJw1KZ*bQ^a>B)9 zo1!3=R~;`R_W+%UcQy^tqKgB5zXTbT%f~N0S)IAA4bfHVd&qj>TfWj++Fz6kMJ_f- zSsgLA{obZx%P-0aKwEK{dx`OuCj)S*E+NGho_ttUlcPrKlaNF>*;E?zW-ksYHO^A5 zgQuMA>qwPyFPQD096DQ`Ad+^^NyQ#EP^=@p!$?q>AL(F1k>*kTJ28=r0q*)naz~YP z0Iu!4b}Y#xKrA9dz!;J;$Xq(3)Uw9}n%woyDq>;mK5mpoRa118}E zq#RNQKcTodBv1!ud*wdYP<}F3yZMpHh#hagz_3@`ynLy;+fXudsi!4OX(Lbg9h6p~ zuk|lWE%0fBZ*AoIP;@tpNBC65?mBpC)kQZ@)Nl|N_$~S4E}z!k08Z`!;boy}If=iy zcE3S}nuTElpj*tZb&%={V9vD{smJQzDA(NY$n3VgT*3UC3DX1g_Nn3B`n7}G+YopJ zDNoY=4)=hWIR$3s+~f@71cpeDTm!qpch5chch7V-!RBm6gZGpL zh9Gi$v`l`>vkRJBjoufz0QmijGoeE=*XB_pjWn4)bDJ^NwEXLCjI6q0k}uqiuTMe7j=%ILTM{+>9YpZ z5HoEx;EVq~{+s(A*%hUOZCcq!r&XFi5C4%Z*n9HCOR1q_Y{gTYxo z(pc=IXHI-og0>?^0@V})a0LNY6u@^pzQF+9Vzv6R-QC^TD-p+li4*xeJm3drVecW6 zrF~tbr}QdfdUK(01ZL*FHPDHR+VF&C{kGK z%Fn!2W~ngyA0UxXf%k`WC)39}5}dHtK)*GDi$lc&y48VToM(31v&k2V(cJdi*h!;^ zB~etcE-L2w+7?_Em8I;oa!%f<=NzdaC5JzJ072uIT^v-QlAB$t1O2CpSuc)Z_U|iy(qU{eOJ+WH|k`b=@ z*`0D5v2-dX7639Bf=mCQ+od>{4<6#$#&ddNmb&XtPhb`fa;tA4QGZc99kE(DxPv!2 zejs=hw3AbebQim2F4^smD4!A{m6L88^jS9{701Zwv4owwIZ{=A<#{le28&OveIiZc z`~?#N--d&hTOG%8W3d(4#B)85WT~&;Xzhy#`T;}Qt8n>vj8Qg`%Y-poT5!XgwyYoR z|NLf(rCwum&SOLnPv&1p(u+Z^va?aT3_=4TI#0j(JiooHur5CDp6U5az+PEHwpPZw z?u(xG@kitxD$7N2kY(X$`ED{Nmz&kYA_61KSOT-I>d%*6#l7AWYYwtl-;mrOZQ zIuq5XVM-ZwA5@StcWh1&rE}%V6pnsxr?edlj10Ht#7@_=FHv^S(ZX8ev!;$(I3Z1( zPk1PCHvoooe+E#^T@%w*+zo&|aS~!PLw03@#I@hX)@3h__(kac;q6!YU$I34Ym{B# z^MogDEZ#00N(?)L{93oCobDUV&tDQ{@y<;cT$2j`y&A-u+IZpSD)h->JNO z&gj8GjUQmjvo=p4RJm+CE3GKKA5Kto>4(3XtMj;{%?}1s_Wz77{&({@kRkmWeEt4l zYV6vTXMvXk&&mJT8c-t{Io-1xRwPjeKc~4}oBNA!*?ut3=03L2le}&OUQlngfn`iw z+(RkkKcf3~TKWVFA@_dBKsr?{bbBrVu>Xm_i^ml?Q@-E#eIL;G;0y4YpLdf34Koz& z^t;W$|9U)S#Aau6E(_X1wk^c}_=LvIT~qbu*4Ea-x*u<6*QD@a&8!Huu_^@>>|Odl zJn!E!)AyEd?P2FJ2$z+9C+{Xq2*1A!9#~rJ*-TDs9Nka$r~hXB$N6@KNp$z~CX~E= zQTBD0ybt**PVxRrp#8?lPoh|-m4(b~pe9Z3yoZ#y8 zYT^RuLL~s;_x6I~;X@oL%_ih+i|pTX?ShVPjr?ka3sMZ+O^m(s(GO=srr zJr~`d(|Nl@9cGKV39n7&-jmmRU(k9L_!p2p*DBR0K+U8&fW)Hj=ftD@`yG?O{ca2Q5^U1M1?hVE?+39PoD z(Q9+kn~bNMtj10D7c_c$bw{VDDtew7yODHN6-_P7+PmKH8W0XPn?5n+lTE_yzH`-1 z96WZ)gxNbA(lzNcv?bLf`@AUbo~fgSOZljmBD!W$S}J4V){A_Ml-c0}8Tp^{vIX0e$auR`tvC@KZLrJb z{5Yz`oI0m0sAx>eTq%Hm*xYkmHCRYEePVNI6OuZx;N>ssipw93RzTF$Fihe#drn89 zDu$Aved=sB>JxipJYq;4zq#Y>+)zKd?F0JC+8o~|YZQ(CT3fp|l*F8KOH_Zz_UPnR zMK3U`zlyFR)xSilpY_Ga3}1=yXRb9w*mr$-J{Ip*BRkR*CO^`Iy3-Uc;)G&;>?mEe zhz~iBwX1B@FTF;*E^hVfQG8Zht!-kgjoMIKPMi~4lqbp}nOx;7y#xH{XkYs+U(+_= FKL80vQ0M>v literal 0 HcmV?d00001 diff --git a/docs/search/search-ai-assistant/index.asciidoc b/docs/search/search-ai-assistant/index.asciidoc index b4013d9ef0ce..146dd22f2ff5 100644 --- a/docs/search/search-ai-assistant/index.asciidoc +++ b/docs/search/search-ai-assistant/index.asciidoc @@ -1,5 +1,143 @@ [role="xpack"] -[[search-assistant]] -== Search AI Assistant +[[search-ai-assistant]] +== AI Assistant -(coming in 8.16.0) \ No newline at end of file +[TIP] +==== +Don't confuse AI Assistant with <>! Use Playground to chat with your data, test and tweak different {es} queries in the Playground UI, and download the code to integrate into your own RAG application. + +Use AI Assistant to get help with Elasticsearch and Kibana tasks directly in the UI. +==== + +.Observability use cases +**** +Refer to the {observability-guide}/obs-ai-assistant.html[Observability documentation] for more information on how to use AI Assistant in Observability contexts. +**** + +*AI Assistant for Observability and Search* uses generative AI to help you with a variety of tasks related to Elasticsearch and Kibana, including: + +1. *Constructing Queries*: Assists you in building queries to search and analyze your data. +2. *Indexing Data*: Guides you on how to index data into Elasticsearch. +3. *Searching Data*: Helps you search for specific data within your Elasticsearch indices. +4. *Using Elasticsearch APIs*: Calls Elasticsearch APIs on your behalf if you need specific operations performed. +5. *Generating Sample Data*: Helps you create sample data for testing and development purposes. +6. *Visualizing and Analyzing Data*: Assists you in creating visualizations and analyzing your data using Kibana. +7. *Explaining ES|QL*: Explains how ES|QL works and help you convert queries from other languages to {ref}/esql.html[ES|QL.] + +[discrete] +[[ai-assistant-requirements]] +=== Requirements + +To use AI Assistant in *Search* contexts, you must have the following: + +* Elastic Stack version 8.16.0, or an Elasticsearch Serverless project. +* A <> to connect to a LLM provider, or a local model. +** You need an account with a third-party generative AI provider, which AI Assistant uses to generate responses, or else you need to host your own local model. +** To set up AI Assistant, you need the `Actions and Connectors : All` <>. +* To use AI Assistant, you need at least the `Elastic AI Assistant : All` and `Actions and Connectors : Read` <>. +* AI Assistant requires {ml-docs}/ml-nlp-elser.html[ELSER], Elastic's proprietary semantic search model. + +[discrete] +[[ai-assistant-data-information]] +=== Your data and AI Assistant + +Elastic does not use customer data for model training. This includes anything you send the model, such as alert or event data, detection rule configurations, queries, and prompts. However, any data you provide to AI Assistant will be processed by the third-party provider you chose when setting up the generative AI connector as part of the assistant setup. + +Elastic does not control third-party tools, and assumes no responsibility or liability for their content, operation, or use, nor for any loss or damage that may arise from your using such tools. Please exercise caution when using AI tools with personal, sensitive, or confidential information. Any data you submit may be used by the provider for AI training or other purposes. There is no guarantee that the provider will keep any information you provide secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use. + +[discrete] +[[ai-assistant-using]] +=== Using AI Assistant + +To open AI Assistant, select the **AI Assistant** button in the top toolbar in the UI. +You can also use the global search field in the UI to find AI Assistant. +// <> +// TODO link will be available once https://github.com/elastic/kibana/pull/199352 is merged. + +[role="screenshot"] +image::images/ai-assistant-button.png[AI Assistant button,50] + +This opens the AI Assistant chat interface flyout. + +[role="screenshot] +image::images/ai-assistant-welcome-chat.png[AI Assistant Welcome chat,450] + +You can get started by selecting *✨ Suggest* to get some example prompts, or by typing into the chat field. + +[discrete] +[[ai-assistant-add-custom-data]] +=== Add data to the AI Assistant knowledge base + +[NOTE] +==== +This functionality is not available on Elastic Cloud Serverless projects. +==== + +You can improve the relevance of AI Assistant’s responses by indexing your own data into AI Assistant's knowledge base. +AI Assistant uses {ml-docs}/ml-nlp-elser.html[ELSER], Elastic's proprietary semantic search model, to power its search capabilities. + +[discrete] +[[search-ai-assistant-use-the-ui]] +==== Use the UI + +To add external data to the knowledge base in UI: + +. In the AI Assistant UI, select the **Settings** icon: `⋮`. +. Under *Actions*, click **Manage knowledge base**. +. Click the **New entry** button, and choose either: ++ +** **Single entry**: Write content for a single entry in the UI. +** **Bulk import**: Upload a newline delimited JSON (`ndjson`) file containing a list of entries to add to the knowledge base. +Each object should conform to the following format: ++ +[source,json] +---- +{ + "id": "a_unique_human_readable_id", + "text": "Contents of item", +} +---- + +[discrete] +[[observability-ai-assistant-add-data-to-kb]] +==== Use Search connectors + +// Will be updated to mention reindex option for arbitrary indices +// Need to consolidate docs with obs team first + +[NOTE] +==== +This functionality is not available on Elastic Cloud Serverless projects. +==== + +You can ingest external data (GitHub issues, Markdown files, Jira tickets, text files, etc.) into {es} using {ref}/es-connectors.html[Search Connectors]. Connectors sync third party data sources to {es}. + +Supported service types include {ref}/es-connectors-github.html[GitHub], {ref}/es-connectors-slack.html[Slack], {ref}/es-connectors-jira.html[Jira], and more. These can be Elastic managed or self-managed on your own infrastructure. + +To create a connector and make its content available to the AI Assistant knowledge base, follow these steps: + +. *In {kib} UI, go to _Search -> Content -> Connectors_ and follow the instructions to create a new connector.* ++ +For example, if you create a {ref}/es-connectors-github.html[GitHub connector] you must set a `name`, attach it to a new or existing `index`, add your `personal access token` and include the `list of repositories` to synchronize. ++ +TIP: Learn more about configuring and {ref}/es-connectors-usage.html[using connectors] in the Elasticsearch documentation. ++ +. *Create a pipeline and process the data with ELSER.* ++ +To process connector data using {ml-docs}/ml-nlp-elser.html[ELSER], you must create an *ML Inference Pipeline*: ++ +.. Open the previously created connector and select the *Pipelines* tab. +.. Select *Copy and customize* button at the `Unlock your custom pipelines` box. +.. Select *Add Inference Pipeline* button at the `Machine Learning Inference Pipelines` box. +.. Select *ELSER (Elastic Learned Sparse EncodeR)* ML model to add the necessary embeddings to the data. +.. Select the fields that need to be evaluated as part of the inference pipeline. +.. Test and save the inference pipeline and the overall pipeline. +. *Sync data.* ++ +Once the pipeline is set up, perform a *Full Content Sync* of the connector. The inference pipeline will process the data as follows: ++ +* As data comes in, the ELSER model processes the data, creating sparse embeddings for each document. +* If you inspect the ingested documents, you can see how the weights and tokens are added to the `predicted_value` field. +. *Confirm AI Assistant can access the index.* ++ +Ask the AI Assistant a specific question to confirm that the data is available for the AI Assistant knowledge base. From 4a16e910e95d62fd4cc52f4a870147d691b1a681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:28:02 +0000 Subject: [PATCH 043/100] [Inventory][ECO] Use ControlGroupRenderer to filter by entity types (#199174) closes https://github.com/elastic/kibana/issues/193397 https://github.com/user-attachments/assets/e78639a8-bc63-4c5a-8676-0ad9b5f0563e - Added `Entity type` control group field on the Inventory page. - Added `Filters` buttons to the Unified Search bar on the Inventory oage - Moved common hooks from infra to Obs-shared - Refactoring --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-optimizer/limits.yml | 2 +- .../hooks/use_abortable_async.ts | 4 +- .../hooks/use_asset_details_url_state.ts | 2 +- .../infra/public/hooks/use_inventory_views.ts | 3 +- .../hooks/use_metrics_explorer_views.ts | 3 +- ...log_entry_categories_results_url_state.tsx | 2 +- .../use_log_entry_rate_results_url_state.tsx | 2 +- .../infra/public/pages/logs/stream/page.tsx | 3 +- .../search_bar/control_panels_config.ts | 41 +++++ .../search_bar/controls_content.tsx | 13 +- .../components/search_bar/controls_title.tsx | 2 +- .../hosts/hooks/use_hosts_table_url_state.ts | 2 +- .../hosts/hooks/use_logs_search_url_state.ts | 2 +- .../pages/metrics/hosts/hooks/use_tab_id.ts | 2 +- .../metrics/hosts/hooks/use_unified_search.ts | 2 +- .../hooks/use_unified_search_url_state.ts | 2 +- .../use_asset_details_flyout_url_state.ts | 2 +- .../hooks/use_waffle_filters.ts | 2 +- .../hooks/use_waffle_options.ts | 2 +- .../inventory_view/hooks/use_waffle_time.ts | 2 +- .../metric_detail/hooks/use_metrics_time.ts | 2 +- .../inventory/common/entities.ts | 25 +-- .../inventory/common/entitites.test.ts | 57 ------ .../inventory/e2e/cypress/e2e/home.cy.ts | 33 ++-- .../inventory/kibana.jsonc | 2 +- .../public/components/app_root/index.tsx | 12 +- .../badge_filter_with_popover.test.tsx | 28 +-- .../badge_filter_with_popover/index.tsx | 121 +++++++------ .../entities_grid/entities_grid.stories.tsx | 2 - .../public/components/entities_grid/index.tsx | 13 +- .../grouped_entities_grid.tsx | 71 ++++---- .../components/grouped_inventory/index.tsx | 36 ++-- .../inventory_group_accordion.test.tsx | 8 +- .../inventory_group_accordion.tsx | 26 +-- .../components/search_bar/control_groups.tsx | 98 +++++++++++ .../search_bar/entity_types_controls.tsx | 67 ------- .../public/components/search_bar/index.tsx | 81 +++------ .../index.tsx} | 58 +++--- .../public/hooks/use_discover_redirect.ts | 36 ++-- .../hooks/use_unified_search_context.ts | 166 ++++++++++++++++++ .../public/hooks/use_unified_search_url.ts | 100 +++++++++++ .../public/pages/inventory_page/index.tsx | 30 +--- .../inventory/public/routes/config.tsx | 11 +- .../services/telemetry/telemetry_client.ts | 7 - .../services/telemetry/telemetry_events.ts | 34 ---- .../telemetry/telemetry_service.test.ts | 22 --- .../public/services/telemetry/types.ts | 8 - .../routes/entities/get_entity_groups.ts | 25 +-- .../routes/entities/get_latest_entities.ts | 15 +- .../entities/get_latest_entities_alerts.ts | 8 +- .../inventory/server/routes/entities/route.ts | 15 +- .../inventory/tsconfig.json | 2 + .../components/log_stream/log_stream.tsx | 2 +- .../public/utils/use_kibana_query_settings.ts | 31 ---- .../hooks/use_control_panels_url_state.ts | 158 +++++++---------- .../public/hooks/use_kibana_query_settings.ts | 0 .../public/hooks/use_url_state.ts | 24 ++- .../observability_shared/public/index.ts | 4 + .../observability_shared/tsconfig.json | 1 + .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 62 files changed, 783 insertions(+), 757 deletions(-) create mode 100644 x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/control_panels_config.ts delete mode 100644 x-pack/plugins/observability_solution/inventory/common/entitites.test.ts create mode 100644 x-pack/plugins/observability_solution/inventory/public/components/search_bar/control_groups.tsx delete mode 100644 x-pack/plugins/observability_solution/inventory/public/components/search_bar/entity_types_controls.tsx rename x-pack/plugins/observability_solution/inventory/public/components/{grouped_inventory/unified_inventory.tsx => unified_inventory/index.tsx} (71%) create mode 100644 x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_context.ts create mode 100644 x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_url.ts delete mode 100644 x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts rename x-pack/plugins/observability_solution/{infra/public/pages/metrics/hosts => observability_shared/public}/hooks/use_control_panels_url_state.ts (63%) rename x-pack/plugins/observability_solution/{infra => observability_shared}/public/hooks/use_kibana_query_settings.ts (100%) rename x-pack/plugins/observability_solution/{infra => observability_shared}/public/hooks/use_url_state.ts (81%) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index df8a077e844f..aca3a1a0c3c9 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -119,7 +119,7 @@ pageLoadAssetSize: observabilityAiAssistantManagement: 19279 observabilityLogsExplorer: 46650 observabilityOnboarding: 19573 - observabilityShared: 80000 + observabilityShared: 111036 osquery: 107090 painlessLab: 179748 presentationPanel: 55463 diff --git a/x-pack/packages/observability/observability_utils/hooks/use_abortable_async.ts b/x-pack/packages/observability/observability_utils/hooks/use_abortable_async.ts index 433ca877b0f6..477d765ef7a7 100644 --- a/x-pack/packages/observability/observability_utils/hooks/use_abortable_async.ts +++ b/x-pack/packages/observability/observability_utils/hooks/use_abortable_async.ts @@ -54,7 +54,9 @@ export function useAbortableAsync( }) .catch((err) => { setValue(undefined); - setError(err); + if (!controller.signal.aborted) { + setError(err); + } }) .finally(() => setLoading(false)); } else { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts index d0694ef7f207..3199cbf70c0b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts @@ -15,8 +15,8 @@ import { ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED, } from '@kbn/rule-data-utils'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { ContentTabIds } from '../types'; -import { useUrlState } from '../../../hooks/use_url_state'; import { ASSET_DETAILS_URL_STATE_KEY } from '../constants'; import { ALERT_STATUS_ALL } from '../../shared/alerts/constants'; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts index 38f3b1960410..d334baa0e5fd 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts @@ -9,7 +9,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useUiTracker } from '@kbn/observability-shared-plugin/public'; +import { useUiTracker, useUrlState } from '@kbn/observability-shared-plugin/public'; import { MutationContext, SavedViewResult, @@ -23,7 +23,6 @@ import { } from '../../common/http_api/latest'; import type { InventoryView } from '../../common/inventory_views'; import { useKibanaContextForPlugin } from './use_kibana'; -import { useUrlState } from './use_url_state'; import { useSavedViewsNotifier } from './use_saved_views_notifier'; import { useSourceContext } from '../containers/metrics_source'; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts index 6d652af02a13..ddf27da96e1a 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts @@ -9,7 +9,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useUiTracker } from '@kbn/observability-shared-plugin/public'; +import { useUiTracker, useUrlState } from '@kbn/observability-shared-plugin/public'; import { MutationContext, @@ -23,7 +23,6 @@ import { UpdateMetricsExplorerViewAttributesRequestPayload, } from '../../common/http_api/latest'; import { MetricsExplorerView } from '../../common/metrics_explorer_views'; -import { useUrlState } from './use_url_state'; import { useSavedViewsNotifier } from './use_saved_views_notifier'; import { useSourceContext } from '../containers/metrics_source'; import { useKibanaContextForPlugin } from './use_kibana'; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx index 169000aa3801..f219757da851 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results_url_state.tsx @@ -9,7 +9,7 @@ import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { useUrlState } from '../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { useKibanaTimefilterTime, useSyncKibanaTimeFilterTime, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx index 1130c8dca9be..f669d82f76f0 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results_url_state.tsx @@ -12,8 +12,8 @@ import moment from 'moment'; import * as rt from 'io-ts'; import type { TimeRange as KibanaTimeRange } from '@kbn/es-query'; import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { TimeRange } from '../../../../common/time/time_range'; -import { useUrlState } from '../../../hooks/use_url_state'; import { useKibanaTimefilterTime, useSyncKibanaTimeFilterTime, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page.tsx index 33ff9300c4d9..706fb7811fa7 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/stream/page.tsx @@ -6,7 +6,7 @@ */ import { EuiErrorBoundary } from '@elastic/eui'; -import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; +import { useKibanaQuerySettings, useTrackPageview } from '@kbn/observability-shared-plugin/public'; import React from 'react'; import { useLogViewContext } from '@kbn/logs-shared-plugin/public'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -14,7 +14,6 @@ import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; import { LogStreamPageStateProvider } from '../../../observability_logs/log_stream_page/state'; import { streamTitle } from '../../../translations'; import { useKbnUrlStateStorageFromRouterContext } from '../../../containers/kbn_url_state_context'; -import { useKibanaQuerySettings } from '../../../hooks/use_kibana_query_settings'; import { ConnectedStreamPageContent } from './page_content'; export const StreamPage = () => { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/control_panels_config.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/control_panels_config.ts new file mode 100644 index 000000000000..75e246997476 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/control_panels_config.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ControlPanels } from '@kbn/observability-shared-plugin/public'; + +export const availableControlsPanels = { + HOST_OS_NAME: 'host.os.name', + CLOUD_PROVIDER: 'cloud.provider', + SERVICE_NAME: 'service.name', +}; + +export const controlPanelConfigs: ControlPanels = { + [availableControlsPanels.HOST_OS_NAME]: { + order: 0, + width: 'medium', + grow: false, + type: 'optionsListControl', + fieldName: availableControlsPanels.HOST_OS_NAME, + title: 'Operating System', + }, + [availableControlsPanels.CLOUD_PROVIDER]: { + order: 1, + width: 'medium', + grow: false, + type: 'optionsListControl', + fieldName: availableControlsPanels.CLOUD_PROVIDER, + title: 'Cloud Provider', + }, + [availableControlsPanels.SERVICE_NAME]: { + order: 2, + width: 'medium', + grow: false, + type: 'optionsListControl', + fieldName: availableControlsPanels.SERVICE_NAME, + title: 'Service Name', + }, +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx index 2ee6aa762e77..847d0e05183a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_content.tsx @@ -5,18 +5,19 @@ * 2.0. */ -import React, { useCallback, useEffect, useRef } from 'react'; import { ControlGroupRenderer, ControlGroupRendererApi, - DataControlApi, ControlGroupRuntimeState, + DataControlApi, } from '@kbn/controls-plugin/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { DataView } from '@kbn/data-views-plugin/public'; -import { Subscription } from 'rxjs'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { useControlPanels } from '../../hooks/use_control_panels_url_state'; +import { useControlPanels } from '@kbn/observability-shared-plugin/public'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { Subscription } from 'rxjs'; +import { controlPanelConfigs } from './control_panels_config'; import { ControlTitle } from './controls_title'; interface Props { @@ -34,7 +35,7 @@ export const ControlsContent: React.FC = ({ timeRange, onFiltersChange, }) => { - const [controlPanels, setControlPanels] = useControlPanels(dataView); + const [controlPanels, setControlPanels] = useControlPanels(controlPanelConfigs, dataView); const subscriptions = useRef(new Subscription()); const getInitialInput = useCallback( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_title.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_title.tsx index 7202985dbb6b..b5b45e0d3979 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_title.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/search_bar/controls_title.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { EuiFormLabel, EuiText, EuiLink, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { availableControlsPanels } from '../../hooks/use_control_panels_url_state'; import { Popover } from '../common/popover'; +import { availableControlsPanels } from './control_panels_config'; const helpMessages = { [availableControlsPanels.SERVICE_NAME]: ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts index c3a5117c18ef..3c2569da620c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table_url_state.ts @@ -12,7 +12,7 @@ import { constant, identity } from 'fp-ts/lib/function'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import deepEqual from 'fast-deep-equal'; import { useReducer } from 'react'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { DEFAULT_PAGE_SIZE, LOCAL_STORAGE_PAGE_SIZE_KEY } from '../constants'; export const GET_DEFAULT_TABLE_PROPERTIES: TableProperties = { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_logs_search_url_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_logs_search_url_state.ts index d40004e325a6..e2caa050132d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_logs_search_url_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_logs_search_url_state.ts @@ -9,7 +9,7 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; const DEFAULT_QUERY = { language: 'kuery', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts index ec5d89775148..4e9215158c69 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_tab_id.ts @@ -9,7 +9,7 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; const TAB_ID_URL_STATE_KEY = 'tabId'; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index 8912ec480e3b..8a7302da1a22 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -10,9 +10,9 @@ import { buildEsQuery, Filter, fromKueryExpression, TimeRange, type Query } from import { Subscription, map, tap } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import useEffectOnce from 'react-use/lib/useEffectOnce'; +import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public'; import { useSearchSessionContext } from '../../../../hooks/use_search_session'; import { parseDateRange } from '../../../../utils/datemath'; -import { useKibanaQuerySettings } from '../../../../hooks/use_kibana_query_settings'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts index 4c96fd93524b..c7bcf09271a3 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search_url_state.ts @@ -14,7 +14,7 @@ import { constant, identity } from 'fp-ts/lib/function'; import { enumeration } from '@kbn/securitysolution-io-ts-types'; import { FilterStateStore } from '@kbn/es-query'; import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { useKibanaTimefilterTime, useSyncKibanaTimeFilterTime, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_asset_details_flyout_url_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_asset_details_flyout_url_state.ts index bbce5a9f9057..8878dead99d0 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_asset_details_flyout_url_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_asset_details_flyout_url_state.ts @@ -9,7 +9,7 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; export const GET_DEFAULT_PROPERTIES: AssetDetailsFlyoutProperties = { detailsItemId: null, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts index 94ad32c4e309..7372fbcfdd73 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts @@ -12,12 +12,12 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import createContainter from 'constate'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { type InventoryFiltersState, inventoryFiltersStateRT, } from '../../../../../common/inventory_views'; import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill'; -import { useUrlState } from '../../../../hooks/use_url_state'; import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts index 38dd4f0bf361..41b91bce9c4e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts @@ -11,6 +11,7 @@ import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import createContainer from 'constate'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { InventoryViewOptions } from '../../../../../common/inventory_views/types'; import { type InventoryLegendOptions, @@ -24,7 +25,6 @@ import type { SnapshotGroupBy, SnapshotCustomMetricInput, } from '../../../../../common/http_api/snapshot_api'; -import { useUrlState } from '../../../../hooks/use_url_state'; export const DEFAULT_LEGEND: WaffleLegendOptions = { palette: 'cool', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts index 670f8645fc48..a7aadb731a75 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts @@ -12,7 +12,7 @@ import { fold } from 'fp-ts/lib/Either'; import DateMath from '@kbn/datemath'; import { constant, identity } from 'fp-ts/lib/function'; import createContainer from 'constate'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { useKibanaTimefilterTime } from '../../../../hooks/use_kibana_timefilter_time'; export const DEFAULT_WAFFLE_TIME_STATE: WaffleTimeState = { currentTime: Date.now(), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts index 7c61778dce74..b36a1a1cd1c5 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts @@ -13,8 +13,8 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; import { replaceStateKeyInQueryString } from '../../../../../common/url_state_storage_service'; -import { useUrlState } from '../../../../hooks/use_url_state'; const parseRange = (range: MetricsTimeInput) => { const parsedFrom = dateMath.parse(range.from.toString()); diff --git a/x-pack/plugins/observability_solution/inventory/common/entities.ts b/x-pack/plugins/observability_solution/inventory/common/entities.ts index 507d9d492c0f..3a9684a38254 100644 --- a/x-pack/plugins/observability_solution/inventory/common/entities.ts +++ b/x-pack/plugins/observability_solution/inventory/common/entities.ts @@ -80,29 +80,6 @@ export const ENTITIES_LATEST_ALIAS = entitiesAliasPattern({ dataset: ENTITY_LATEST, }); -const entityArrayRt = t.array(t.string); -export const entityTypesRt = new t.Type( - 'entityTypesRt', - entityArrayRt.is, - (input, context) => { - if (typeof input === 'string') { - const arr = input.split(','); - const validation = entityArrayRt.decode(arr); - if (isRight(validation)) { - return t.success(validation.right); - } - } else if (Array.isArray(input)) { - const validation = entityArrayRt.decode(input); - if (isRight(validation)) { - return t.success(validation.right); - } - } - - return t.failure(input, context); - }, - (arr) => arr.join() -); - export interface Entity { [ENTITY_LAST_SEEN]: string; [ENTITY_ID]: string; @@ -117,7 +94,7 @@ export interface Entity { export type EntityGroup = { count: number; } & { - [key: string]: any; + [key: string]: string; }; export type InventoryEntityLatest = z.infer & { diff --git a/x-pack/plugins/observability_solution/inventory/common/entitites.test.ts b/x-pack/plugins/observability_solution/inventory/common/entitites.test.ts deleted file mode 100644 index c923bda53074..000000000000 --- a/x-pack/plugins/observability_solution/inventory/common/entitites.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { isLeft, isRight } from 'fp-ts/lib/Either'; -import { entityTypesRt } from './entities'; - -const validate = (input: unknown) => entityTypesRt.decode(input); - -describe('entityTypesRt codec', () => { - it('should validate a valid string of entity types', () => { - const input = 'service,host,container'; - const result = validate(input); - expect(isRight(result)).toBe(true); - if (isRight(result)) { - expect(result.right).toEqual(['service', 'host', 'container']); - } - }); - - it('should validate a valid array of entity types', () => { - const input = ['service', 'host', 'container']; - const result = validate(input); - expect(isRight(result)).toBe(true); - if (isRight(result)) { - expect(result.right).toEqual(['service', 'host', 'container']); - } - }); - - it('should fail validation when input is not a string or array', () => { - const input = 123; - const result = validate(input); - expect(isLeft(result)).toBe(true); - }); - - it('should validate an empty array as valid', () => { - const input: unknown[] = []; - const result = validate(input); - expect(isRight(result)).toBe(true); - if (isRight(result)) { - expect(result.right).toEqual([]); - } - }); - - it('should serialize a valid array back to a string', () => { - const input = ['service', 'host']; - const serialized = entityTypesRt.encode(input); - expect(serialized).toBe('service,host'); - }); - - it('should serialize an empty array back to an empty string', () => { - const input: string[] = []; - const serialized = entityTypesRt.encode(input); - expect(serialized).toBe(''); - }); -}); diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts index d24953c38eb1..9c9011609740 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts @@ -165,16 +165,17 @@ describe('Home page', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); + cy.intercept('POST', 'internal/controls/optionsList/entities-*-latest').as( + 'entityTypeControlGroupOptions' + ); cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); - cy.getByTestSubj('entityTypesFilterComboBox') - .click() - .getByTestSubj('entityTypesFilterserviceOption') - .click(); + cy.getByTestSubj('optionsList-control-entity.type').click(); + cy.wait('@entityTypeControlGroupOptions'); + cy.getByTestSubj('optionsList-control-selection-service').click(); cy.wait('@getGroups'); - cy.contains('service'); cy.getByTestSubj('inventoryGroupTitle_entity.type_service').click(); cy.wait('@getEntities'); cy.get('server1').should('not.exist'); @@ -188,16 +189,17 @@ describe('Home page', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); + cy.intercept('POST', 'internal/controls/optionsList/entities-*-latest').as( + 'entityTypeControlGroupOptions' + ); cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); - cy.getByTestSubj('entityTypesFilterComboBox') - .click() - .getByTestSubj('entityTypesFilterhostOption') - .click(); + cy.getByTestSubj('optionsList-control-entity.type').click(); + cy.wait('@entityTypeControlGroupOptions'); + cy.getByTestSubj('optionsList-control-selection-host').click(); cy.wait('@getGroups'); - cy.contains('host'); cy.getByTestSubj('inventoryGroupTitle_entity.type_host').click(); cy.wait('@getEntities'); cy.contains('server1'); @@ -211,16 +213,17 @@ describe('Home page', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', }).as('getEEMStatus'); + cy.intercept('POST', 'internal/controls/optionsList/entities-*-latest').as( + 'entityTypeControlGroupOptions' + ); cy.intercept('GET', '/internal/inventory/entities?**').as('getEntities'); cy.intercept('GET', '/internal/inventory/entities/group_by/**').as('getGroups'); cy.visitKibana('/app/inventory'); cy.wait('@getEEMStatus'); - cy.getByTestSubj('entityTypesFilterComboBox') - .click() - .getByTestSubj('entityTypesFiltercontainerOption') - .click(); + cy.getByTestSubj('optionsList-control-entity.type').click(); + cy.wait('@entityTypeControlGroupOptions'); + cy.getByTestSubj('optionsList-control-selection-container').click(); cy.wait('@getGroups'); - cy.contains('container'); cy.getByTestSubj('inventoryGroupTitle_entity.type_container').click(); cy.wait('@getEntities'); cy.contains('server1').should('not.exist'); diff --git a/x-pack/plugins/observability_solution/inventory/kibana.jsonc b/x-pack/plugins/observability_solution/inventory/kibana.jsonc index e7cc398c9c65..e6e7c5f2fa2f 100644 --- a/x-pack/plugins/observability_solution/inventory/kibana.jsonc +++ b/x-pack/plugins/observability_solution/inventory/kibana.jsonc @@ -21,7 +21,7 @@ "ruleRegistry", "share" ], - "requiredBundles": ["kibanaReact"], + "requiredBundles": ["kibanaReact","controls"], "optionalPlugins": ["spaces", "cloud"], "extraPublicDirs": [] } diff --git a/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx index 6bec4335c719..52f46268da2e 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx @@ -12,12 +12,12 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { RouteRenderer, RouterProvider } from '@kbn/typed-react-router-config'; import React from 'react'; import { InventoryContextProvider } from '../../context/inventory_context_provider'; -import { InventorySearchBarContextProvider } from '../../context/inventory_search_bar_context_provider'; +import { KibanaEnvironment } from '../../hooks/use_kibana'; +import { UnifiedSearchProvider } from '../../hooks/use_unified_search_context'; import { inventoryRouter } from '../../routes/config'; import { InventoryServices } from '../../services/types'; import { InventoryStartDependencies } from '../../types'; import { HeaderActionMenuItems } from './header_action_menu'; -import { KibanaEnvironment } from '../../hooks/use_kibana'; export function AppRoot({ coreStart, @@ -43,12 +43,12 @@ export function AppRoot({ return ( - - + + - - + + ); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/badge_filter_with_popover.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/badge_filter_with_popover.test.tsx index 6018b66d3799..f3c518ef49b1 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/badge_filter_with_popover.test.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/badge_filter_with_popover.test.tsx @@ -5,11 +5,11 @@ * 2.0. */ +import { copyToClipboard } from '@elastic/eui'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; -import { render, fireEvent, screen } from '@testing-library/react'; import { BadgeFilterWithPopover } from '.'; -import { EuiThemeProvider, copyToClipboard } from '@elastic/eui'; -import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; jest.mock('@elastic/eui', () => ({ ...jest.requireActual('@elastic/eui'), @@ -17,10 +17,8 @@ jest.mock('@elastic/eui', () => ({ })); describe('BadgeFilterWithPopover', () => { - const mockOnFilter = jest.fn(); const field = ENTITY_TYPE; const value = 'host'; - const label = 'Host'; const popoverContentDataTestId = 'inventoryBadgeFilterWithPopoverContent'; const popoverContentTitleTestId = 'inventoryBadgeFilterWithPopoverTitle'; @@ -28,32 +26,16 @@ describe('BadgeFilterWithPopover', () => { jest.clearAllMocks(); }); - it('renders the badge with the correct label', () => { - render( - , - { wrapper: EuiThemeProvider } - ); - expect(screen.queryByText(label)).toBeInTheDocument(); - expect(screen.getByText(label).textContent).toBe(label); - }); - it('opens the popover when the badge is clicked', () => { - render(); + render(); expect(screen.queryByTestId(popoverContentDataTestId)).not.toBeInTheDocument(); fireEvent.click(screen.getByText(value)); expect(screen.queryByTestId(popoverContentDataTestId)).toBeInTheDocument(); expect(screen.queryByTestId(popoverContentTitleTestId)?.textContent).toBe(`${field}:${value}`); }); - it('calls onFilter when the "Filter for" button is clicked', () => { - render(); - fireEvent.click(screen.getByText(value)); - fireEvent.click(screen.getByTestId('inventoryBadgeFilterWithPopoverFilterForButton')); - expect(mockOnFilter).toHaveBeenCalled(); - }); - it('copies value to clipboard when the "Copy value" button is clicked', () => { - render(); + render(); fireEvent.click(screen.getByText(value)); fireEvent.click(screen.getByTestId('inventoryBadgeFilterWithPopoverCopyValueButton')); expect(copyToClipboard).toHaveBeenCalledWith(value); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/index.tsx index d1e952e189d6..83e0bb02e6d8 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/badge_filter_with_popover/index.tsx @@ -8,28 +8,29 @@ import { EuiBadge, EuiButtonEmpty, - EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiPopoverFooter, + EuiPopoverTitle, copyToClipboard, useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; import React, { useState } from 'react'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; interface Props { field: string; value: string; - label?: string; - onFilter: () => void; } -export function BadgeFilterWithPopover({ field, value, onFilter, label }: Props) { +export function BadgeFilterWithPopover({ field, value }: Props) { const [isOpen, setIsOpen] = useState(false); const theme = useEuiTheme(); + const { addFilter } = useUnifiedSearchContext(); return ( - {label || value} + {value} } isOpen={isOpen} closePopover={() => setIsOpen(false)} + panelPaddingSize="s" > - - - - - {field}: - - - - {value} - - - + + + + + + {field}: + + + + {value} + + + + + + + { + addFilter({ fieldName: ENTITY_TYPE, operation: '+', value }); + }} + > + {i18n.translate('xpack.inventory.badgeFilterWithPopover.filterForButtonEmptyLabel', { + defaultMessage: 'Filter for', + })} + + + + { + addFilter({ fieldName: ENTITY_TYPE, operation: '-', value }); + }} + > + {i18n.translate('xpack.inventory.badgeFilterWithPopover.filterForButtonEmptyLabel', { + defaultMessage: 'Filter out', + })} + + + - - - - {i18n.translate('xpack.inventory.badgeFilterWithPopover.filterForButtonEmptyLabel', { - defaultMessage: 'Filter for', - })} - - - - copyToClipboard(value)} - > - {i18n.translate('xpack.inventory.badgeFilterWithPopover.copyValueButtonEmptyLabel', { - defaultMessage: 'Copy value', - })} - - - + copyToClipboard(value)} + > + {i18n.translate('xpack.inventory.badgeFilterWithPopover.copyValueButtonEmptyLabel', { + defaultMessage: 'Copy value', + })} + ); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx index 047c2e73d0d3..a3f2834934cd 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/entities_grid.stories.tsx @@ -77,7 +77,6 @@ export const Grid: Story = (args) => { onChangePage={setPageIndex} onChangeSort={setSort} pageIndex={pageIndex} - onFilterByType={(selectedEntityType) => updateArgs({ entityType: selectedEntityType })} /> @@ -100,7 +99,6 @@ export const EmptyGrid: Story = (args) => { onChangePage={setPageIndex} onChangeSort={setSort} pageIndex={pageIndex} - onFilterByType={() => {}} /> ); }; diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx index 7819e944c486..7ca29f782033 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entities_grid/index.tsx @@ -40,7 +40,6 @@ interface Props { pageIndex: number; onChangeSort: (sorting: EuiDataGridSorting['columns'][0]) => void; onChangePage: (nextPage: number) => void; - onFilterByType: (entityType: string) => void; } const PAGE_SIZE = 20; @@ -53,7 +52,6 @@ export function EntitiesGrid({ pageIndex, onChangePage, onChangeSort, - onFilterByType, }: Props) { const { getDiscoverRedirectUrl } = useDiscoverRedirect(); @@ -98,14 +96,7 @@ export function EntitiesGrid({ return entity?.alertsCount ? : null; case ENTITY_TYPE: - return ( - onFilterByType(entityType)} - /> - ); + return ; case ENTITY_LAST_SEEN: return ( { - return inventoryAPIClient.fetch('GET /internal/inventory/entities', { - params: { - query: { - sortDirection, - sortField, - entityTypes: field?.length ? JSON.stringify([field]) : undefined, - kuery, + if (isControlPanelsInitiated) { + return inventoryAPIClient.fetch('GET /internal/inventory/entities', { + params: { + query: { + sortDirection, + sortField, + esQuery: stringifiedEsQuery, + entityTypes: groupValue?.length ? JSON.stringify([groupValue]) : undefined, + }, }, - }, - signal, - }); + signal, + }); + } }, - [field, inventoryAPIClient, kuery, sortDirection, sortField] + [ + groupValue, + inventoryAPIClient, + sortDirection, + sortField, + isControlPanelsInitiated, + stringifiedEsQuery, + ] ); useEffectOnce(() => { @@ -86,7 +94,7 @@ export function GroupedEntitiesGrid({ field }: Props) { ...query, pagination: entityPaginationRt.encode({ ...pagination, - [field]: nextPage, + [groupValue]: nextPage, }), }, }); @@ -103,18 +111,6 @@ export function GroupedEntitiesGrid({ field }: Props) { }); } - function handleTypeFilter(type: string) { - const { pagination: _, ...rest } = query; - inventoryRoute.push('/', { - path: {}, - query: { - ...rest, - // Override the current entity types - entityTypes: [type], - }, - }); - } - return ( ); } diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx index b376200495e4..b939f0fa5c42 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/index.tsx @@ -8,20 +8,18 @@ import { EuiSpacer } from '@elastic/eui'; import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; import React from 'react'; import useEffectOnce from 'react-use/lib/useEffectOnce'; -import { InventoryGroupAccordion } from './inventory_group_accordion'; import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; import { useKibana } from '../../hooks/use_kibana'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; +import { InventoryGroupAccordion } from './inventory_group_accordion'; import { InventorySummary } from './inventory_summary'; -import { useInventoryParams } from '../../hooks/use_inventory_params'; -import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; export function GroupedInventory() { const { services: { inventoryAPIClient }, } = useKibana(); - const { query } = useInventoryParams('/'); - const { kuery, entityTypes } = query; - const { refreshSubject$ } = useInventorySearchBarContext(); + const { refreshSubject$, isControlPanelsInitiated, stringifiedEsQuery } = + useUnifiedSearchContext(); const { value = { groupBy: ENTITY_TYPE, groups: [], entitiesCount: 0 }, @@ -29,20 +27,19 @@ export function GroupedInventory() { loading, } = useInventoryAbortableAsync( ({ signal }) => { - return inventoryAPIClient.fetch('GET /internal/inventory/entities/group_by/{field}', { - params: { - path: { - field: ENTITY_TYPE, - }, - query: { - kuery, - entityTypes: entityTypes?.length ? JSON.stringify(entityTypes) : undefined, + if (isControlPanelsInitiated) { + return inventoryAPIClient.fetch('GET /internal/inventory/entities/group_by/{field}', { + params: { + path: { + field: ENTITY_TYPE, + }, + query: { esQuery: stringifiedEsQuery }, }, - }, - signal, - }); + signal, + }); + } }, - [entityTypes, inventoryAPIClient, kuery] + [inventoryAPIClient, stringifiedEsQuery, isControlPanelsInitiated] ); useEffectOnce(() => { @@ -58,8 +55,9 @@ export function GroupedInventory() { {value.groups.map((group) => ( ))} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx index 2cddbb8e46d7..bf0b7064033f 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.test.tsx @@ -25,7 +25,13 @@ describe('Grouped Inventory Accordion', () => { }, ], }; - render(); + render( + + ); expect(screen.getByText(props.groups[0]['entity.type'])).toBeInTheDocument(); const container = screen.getByTestId('inventoryPanelBadgeEntitiesCount_entity.type_host'); expect(within(container).getByText('Entities:')).toBeInTheDocument(); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx index 4c5d34e5a028..0b4e9a46d428 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/inventory_group_accordion.tsx @@ -4,12 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import { css } from '@emotion/react'; import { EuiAccordion, EuiPanel, EuiSpacer, EuiTitle, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useState } from 'react'; import { GroupedEntitiesGrid } from './grouped_entities_grid'; -import type { EntityGroup } from '../../../common/entities'; import { InventoryPanelBadge } from './inventory_panel_badge'; const ENTITIES_COUNT_BADGE = i18n.translate( @@ -18,18 +17,19 @@ const ENTITIES_COUNT_BADGE = i18n.translate( ); export interface InventoryGroupAccordionProps { - group: EntityGroup; groupBy: string; + groupValue: string; + groupCount: number; isLoading?: boolean; } export function InventoryGroupAccordion({ - group, groupBy, + groupValue, + groupCount, isLoading, }: InventoryGroupAccordionProps) { const { euiTheme } = useEuiTheme(); - const field = group[groupBy]; const [open, setOpen] = useState(false); const onToggle = useCallback(() => { @@ -46,19 +46,19 @@ export function InventoryGroupAccordion({ `} > -

{field}

+

{groupValue}

} buttonElement="div" extraAction={ } buttonProps={{ paddingSize: 'm' }} @@ -78,7 +78,7 @@ export function InventoryGroupAccordion({ hasShadow={false} paddingSize="m" > - + )} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/control_groups.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/control_groups.tsx new file mode 100644 index 000000000000..9c263e39562f --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/control_groups.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ControlGroupRenderer, + ControlGroupRendererApi, + ControlGroupRuntimeState, +} from '@kbn/controls-plugin/public'; +import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { ControlPanels, useControlPanels } from '@kbn/observability-shared-plugin/public'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { skip, Subscription } from 'rxjs'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; + +const controlPanelDefinitions: ControlPanels = { + [ENTITY_TYPE]: { + order: 0, + type: 'optionsListControl', + fieldName: ENTITY_TYPE, + width: 'small', + grow: false, + title: 'Type', + }, +}; + +export function ControlGroups() { + const { + isControlPanelsInitiated, + setIsControlPanelsInitiated, + dataView, + searchState, + onPanelFiltersChange, + } = useUnifiedSearchContext(); + const [controlPanels, setControlPanels] = useControlPanels(controlPanelDefinitions, dataView); + const subscriptions = useRef(new Subscription()); + + const getInitialInput = useCallback( + () => async () => { + const initialInput: Partial = { + chainingSystem: 'HIERARCHICAL', + labelPosition: 'oneLine', + initialChildControlState: controlPanels, + }; + + return { initialState: initialInput }; + }, + [controlPanels] + ); + + const loadCompleteHandler = useCallback( + (controlGroup: ControlGroupRendererApi) => { + if (!controlGroup) return; + + subscriptions.current.add( + controlGroup.filters$.pipe(skip(1)).subscribe((newFilters = []) => { + onPanelFiltersChange(newFilters); + }) + ); + + subscriptions.current.add( + controlGroup.getInput$().subscribe(({ initialChildControlState }) => { + if (!isControlPanelsInitiated) { + setIsControlPanelsInitiated(true); + } + setControlPanels(initialChildControlState); + }) + ); + }, + [isControlPanelsInitiated, onPanelFiltersChange, setControlPanels, setIsControlPanelsInitiated] + ); + + useEffect(() => { + const currentSubscriptions = subscriptions.current; + return () => { + currentSubscriptions.unsubscribe(); + }; + }, []); + + if (!dataView) { + return null; + } + + return ( +
+ +
+ ); +} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/entity_types_controls.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/entity_types_controls.tsx deleted file mode 100644 index e2d9dba2709f..000000000000 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/entity_types_controls.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; -import { useInventoryParams } from '../../hooks/use_inventory_params'; -import { useKibana } from '../../hooks/use_kibana'; - -interface Props { - onChange: (entityTypes: string[]) => void; -} - -const toComboBoxOption = (entityType: string): EuiComboBoxOptionOption => ({ - key: entityType, - label: entityType, - 'data-test-subj': `entityTypesFilter${entityType}Option`, -}); - -export function EntityTypesControls({ onChange }: Props) { - const { - query: { entityTypes = [] }, - } = useInventoryParams('/*'); - - const { - services: { inventoryAPIClient }, - } = useKibana(); - - const { value, loading } = useInventoryAbortableAsync( - ({ signal }) => { - return inventoryAPIClient.fetch('GET /internal/inventory/entities/types', { signal }); - }, - [inventoryAPIClient] - ); - - const options = value?.entityTypes.map(toComboBoxOption); - const selectedOptions = entityTypes.map(toComboBoxOption); - - return ( - { - onChange(newOptions.map((option) => option.key).filter((key): key is string => !!key)); - }} - isClearable - /> - ); -} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx index 3a22d9bc19a1..d1ccfd3f358e 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx @@ -4,39 +4,35 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import { SearchBarOwnProps } from '@kbn/unified-search-plugin/public/search_bar'; +import type { SearchBarOwnProps } from '@kbn/unified-search-plugin/public/search_bar'; import deepEqual from 'fast-deep-equal'; import React, { useCallback, useEffect } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { Query } from '@kbn/es-query'; -import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; -import { useInventoryParams } from '../../hooks/use_inventory_params'; import { useKibana } from '../../hooks/use_kibana'; -import { EntityTypesControls } from './entity_types_controls'; -import { DiscoverButton } from './discover_button'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; import { getKqlFieldsWithFallback } from '../../utils/get_kql_field_names_with_fallback'; +import { ControlGroups } from './control_groups'; +import { DiscoverButton } from './discover_button'; export function SearchBar() { - const { refreshSubject$, searchBarContentSubject$, dataView } = useInventorySearchBarContext(); + const { refreshSubject$, dataView, searchState, onQueryChange } = useUnifiedSearchContext(); + const { services: { unifiedSearch, + telemetry, data: { query: { queryString: queryStringService }, }, - telemetry, }, } = useKibana(); - const { - query: { kuery, entityTypes }, - } = useInventoryParams('/*'); - const { SearchBar: UnifiedSearchBar } = unifiedSearch.ui; const syncSearchBarWithUrl = useCallback(() => { - const query = kuery ? { query: kuery, language: 'kuery' } : undefined; + const query = searchState.query; if (query && !deepEqual(queryStringService.getQuery(), query)) { queryStringService.setQuery(query); } @@ -44,67 +40,36 @@ export function SearchBar() { if (!query) { queryStringService.clearQuery(); } - }, [kuery, queryStringService]); + }, [searchState.query, queryStringService]); useEffect(() => { syncSearchBarWithUrl(); }, [syncSearchBarWithUrl]); const registerSearchSubmittedEvent = useCallback( - ({ - searchQuery, - searchIsUpdate, - searchEntityTypes, - }: { - searchQuery?: Query; - searchEntityTypes?: string[]; - searchIsUpdate?: boolean; - }) => { + ({ searchQuery, searchIsUpdate }: { searchQuery?: Query; searchIsUpdate?: boolean }) => { telemetry.reportEntityInventorySearchQuerySubmitted({ kuery_fields: getKqlFieldsWithFallback(searchQuery?.query as string), - entity_types: searchEntityTypes || [], action: searchIsUpdate ? 'submit' : 'refresh', }); }, [telemetry] ); - const registerEntityTypeFilteredEvent = useCallback( - ({ filterEntityTypes, filterKuery }: { filterEntityTypes: string[]; filterKuery?: string }) => { - telemetry.reportEntityInventoryEntityTypeFiltered({ - entity_types: filterEntityTypes, - kuery_fields: filterKuery ? getKqlFieldsWithFallback(filterKuery) : [], - }); - }, - [telemetry] - ); - - const handleEntityTypesChange = useCallback( - (nextEntityTypes: string[]) => { - searchBarContentSubject$.next({ kuery, entityTypes: nextEntityTypes }); - registerEntityTypeFilteredEvent({ filterEntityTypes: nextEntityTypes, filterKuery: kuery }); - }, - [kuery, registerEntityTypeFilteredEvent, searchBarContentSubject$] - ); - const handleQuerySubmit = useCallback>( - ({ query }, isUpdate) => { - searchBarContentSubject$.next({ - kuery: query?.query as string, - entityTypes, - }); + ({ query = { language: 'kuery', query: '' } }, isUpdate) => { + if (isUpdate) { + onQueryChange(query); + } else { + refreshSubject$.next(); + } registerSearchSubmittedEvent({ searchQuery: query, - searchEntityTypes: entityTypes, searchIsUpdate: isUpdate, }); - - if (!isUpdate) { - refreshSubject$.next(); - } }, - [searchBarContentSubject$, entityTypes, registerSearchSubmittedEvent, refreshSubject$] + [registerSearchSubmittedEvent, onQueryChange, refreshSubject$] ); return ( @@ -113,15 +78,17 @@ export function SearchBar() { } + renderQueryInputAppend={() => } onQuerySubmit={handleQuerySubmit} placeholder={i18n.translate('xpack.inventory.searchBar.placeholder', { defaultMessage: 'Search for your entities by name or its metadata (e.g. entity.type : service)', })} + showDatePicker={false} + showFilterBar + showQueryInput + showQueryMenu /> diff --git a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.tsx b/x-pack/plugins/observability_solution/inventory/public/components/unified_inventory/index.tsx similarity index 71% rename from x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.tsx rename to x-pack/plugins/observability_solution/inventory/public/components/unified_inventory/index.tsx index 05f7437a32c4..1bec6dee990d 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/grouped_inventory/unified_inventory.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/unified_inventory/index.tsx @@ -5,21 +5,21 @@ * 2.0. */ import { EuiDataGridSorting } from '@elastic/eui'; +import { decodeOrThrow } from '@kbn/io-ts-utils'; import React from 'react'; import useEffectOnce from 'react-use/lib/useEffectOnce'; -import { decodeOrThrow } from '@kbn/io-ts-utils'; import { - type EntityColumnIds, entityPaginationRt, + type EntityColumnIds, type EntityPagination, } from '../../../common/entities'; -import { EntitiesGrid } from '../entities_grid'; import { useInventoryAbortableAsync } from '../../hooks/use_inventory_abortable_async'; import { useInventoryParams } from '../../hooks/use_inventory_params'; import { useInventoryRouter } from '../../hooks/use_inventory_router'; import { useKibana } from '../../hooks/use_kibana'; -import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; -import { InventorySummary } from './inventory_summary'; +import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; +import { EntitiesGrid } from '../entities_grid'; +import { InventorySummary } from '../grouped_inventory/inventory_summary'; const paginationDecoder = decodeOrThrow(entityPaginationRt); @@ -27,9 +27,11 @@ export function UnifiedInventory() { const { services: { inventoryAPIClient }, } = useKibana(); - const { refreshSubject$ } = useInventorySearchBarContext(); + const { refreshSubject$, isControlPanelsInitiated, stringifiedEsQuery } = + useUnifiedSearchContext(); const { query } = useInventoryParams('/'); - const { sortDirection, sortField, kuery, entityTypes, pagination: paginationQuery } = query; + const { sortDirection, sortField, pagination: paginationQuery } = query; + let pagination: EntityPagination | undefined = {}; const inventoryRoute = useInventoryRouter(); try { @@ -38,9 +40,7 @@ export function UnifiedInventory() { inventoryRoute.push('/', { path: {}, query: { - sortField, - sortDirection, - kuery, + ...query, pagination: undefined, }, }); @@ -55,24 +55,24 @@ export function UnifiedInventory() { refresh, } = useInventoryAbortableAsync( ({ signal }) => { - return inventoryAPIClient.fetch('GET /internal/inventory/entities', { - params: { - query: { - sortDirection, - sortField, - entityTypes: entityTypes?.length ? JSON.stringify(entityTypes) : undefined, - kuery, + if (isControlPanelsInitiated) { + return inventoryAPIClient.fetch('GET /internal/inventory/entities', { + params: { + query: { + sortDirection, + sortField, + esQuery: stringifiedEsQuery, + }, }, - }, - signal, - }); + signal, + }); + } }, - [entityTypes, inventoryAPIClient, kuery, sortDirection, sortField] + [inventoryAPIClient, sortDirection, sortField, isControlPanelsInitiated, stringifiedEsQuery] ); useEffectOnce(() => { const refreshSubscription = refreshSubject$.subscribe(refresh); - return () => refreshSubscription.unsubscribe(); }); @@ -100,19 +100,6 @@ export function UnifiedInventory() { }); } - function handleTypeFilter(type: string) { - const { pagination: _, ...rest } = query; - - inventoryRoute.push('/', { - path: {}, - query: { - ...rest, - // Override the current entity types - entityTypes: [type], - }, - }); - } - return ( <> @@ -124,7 +111,6 @@ export function UnifiedInventory() { onChangePage={handlePageChange} onChangeSort={handleSortChange} pageIndex={pageIndex} - onFilterByType={handleTypeFilter} /> ); diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_discover_redirect.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_discover_redirect.ts index 3c6ba331ec2a..c29caca7e5b7 100644 --- a/x-pack/plugins/observability_solution/inventory/public/hooks/use_discover_redirect.ts +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_discover_redirect.ts @@ -5,19 +5,16 @@ * 2.0. */ import { - ENTITY_TYPE, ENTITY_DEFINITION_ID, ENTITY_DISPLAY_NAME, ENTITY_LAST_SEEN, + ENTITY_TYPE, } from '@kbn/observability-shared-plugin/common'; import { useCallback } from 'react'; -import { type PhrasesFilter, buildPhrasesFilter } from '@kbn/es-query'; -import type { DataViewField } from '@kbn/data-views-plugin/public'; import type { Entity, EntityColumnIds } from '../../common/entities'; import { unflattenEntity } from '../../common/utils/unflatten_entity'; import { useKibana } from './use_kibana'; -import { useInventoryParams } from './use_inventory_params'; -import { useInventorySearchBarContext } from '../context/inventory_search_bar_context_provider'; +import { useUnifiedSearchContext } from './use_unified_search_context'; const ACTIVE_COLUMNS: EntityColumnIds[] = [ENTITY_DISPLAY_NAME, ENTITY_TYPE, ENTITY_LAST_SEEN]; @@ -25,32 +22,22 @@ export const useDiscoverRedirect = () => { const { services: { share, application, entityManager }, } = useKibana(); - const { - query: { kuery, entityTypes }, - } = useInventoryParams('/*'); - const { dataView } = useInventorySearchBarContext(); + const { + dataView, + searchState: { query, filters, panelFilters }, + } = useUnifiedSearchContext(); const discoverLocator = share.url.locators.get('DISCOVER_APP_LOCATOR'); const getDiscoverEntitiesRedirectUrl = useCallback( (entity?: Entity) => { - const filters: PhrasesFilter[] = []; - - const entityTypeField = (dataView?.getFieldByName(ENTITY_TYPE) ?? - entity?.[ENTITY_TYPE]) as DataViewField; - - if (entityTypes && entityTypeField && dataView) { - const entityTypeFilter = buildPhrasesFilter(entityTypeField, entityTypes, dataView); - filters.push(entityTypeFilter); - } - const entityKqlFilter = entity ? entityManager.entityClient.asKqlFilter(unflattenEntity(entity)) : ''; const kueryWithEntityDefinitionFilters = [ - kuery, + query.query, entityKqlFilter, `${ENTITY_DEFINITION_ID} : builtin*`, ] @@ -62,17 +49,18 @@ export const useDiscoverRedirect = () => { indexPatternId: dataView?.id ?? '', columns: ACTIVE_COLUMNS, query: { query: kueryWithEntityDefinitionFilters, language: 'kuery' }, - filters, + filters: [...filters, ...panelFilters], }) : undefined; }, [ application.capabilities.discover?.show, + dataView?.id, discoverLocator, entityManager.entityClient, - entityTypes, - kuery, - dataView, + filters, + panelFilters, + query.query, ] ); diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_context.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_context.ts new file mode 100644 index 000000000000..94df3a035f3b --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_context.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { buildEsQuery, type Filter, fromKueryExpression, type Query } from '@kbn/es-query'; +import createContainer from 'constate'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { map, Subject, Subscription, tap } from 'rxjs'; +import { generateFilters } from '@kbn/data-plugin/public'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; +import deepEqual from 'fast-deep-equal'; +import { i18n } from '@kbn/i18n'; +import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public'; +import { useAdHocInventoryDataView } from './use_adhoc_inventory_data_view'; +import { useUnifiedSearchUrl } from './use_unified_search_url'; +import { useKibana } from './use_kibana'; + +function useUnifiedSearch() { + const [isControlPanelsInitiated, setIsControlPanelsInitiated] = useState(false); + const { dataView } = useAdHocInventoryDataView(); + const [refreshSubject$] = useState>(new Subject()); + const { searchState, setSearchState } = useUnifiedSearchUrl(); + const kibanaQuerySettings = useKibanaQuerySettings(); + const { + services: { + data: { + query: { filterManager: filterManagerService, queryString: queryStringService }, + }, + notifications, + }, + } = useKibana(); + + useEffectOnce(() => { + if (!deepEqual(filterManagerService.getFilters(), searchState.filters)) { + filterManagerService.setFilters( + searchState.filters.map((item) => ({ + ...item, + meta: { ...item.meta, index: dataView?.id }, + })) + ); + } + + if (!deepEqual(queryStringService.getQuery(), searchState.query)) { + queryStringService.setQuery(searchState.query); + } + }); + + useEffect(() => { + const subscription = new Subscription(); + subscription.add( + filterManagerService + .getUpdates$() + .pipe( + map(() => filterManagerService.getFilters()), + tap((filters) => setSearchState({ type: 'SET_FILTERS', filters })) + ) + .subscribe() + ); + + subscription.add( + queryStringService + .getUpdates$() + .pipe( + map(() => queryStringService.getQuery() as Query), + tap((query) => setSearchState({ type: 'SET_QUERY', query })) + ) + .subscribe() + ); + + return () => { + subscription.unsubscribe(); + }; + }, [filterManagerService, queryStringService, setSearchState]); + + const validateQuery = useCallback( + (query: Query) => { + fromKueryExpression(query.query, kibanaQuerySettings); + }, + [kibanaQuerySettings] + ); + + const onQueryChange = useCallback( + (query: Query) => { + try { + validateQuery(query); + setSearchState({ type: 'SET_QUERY', query }); + } catch (e) { + const err = e as Error; + notifications.toasts.addDanger({ + title: i18n.translate('xpack.inventory.unifiedSearchContext.queryError', { + defaultMessage: 'Error while updating the new query', + }), + text: err.message, + }); + } + }, + [validateQuery, setSearchState, notifications.toasts] + ); + + const onPanelFiltersChange = useCallback( + (panelFilters: Filter[]) => { + setSearchState({ type: 'SET_PANEL_FILTERS', panelFilters }); + }, + [setSearchState] + ); + + const onFiltersChange = useCallback( + (filters: Filter[]) => { + setSearchState({ type: 'SET_FILTERS', filters }); + }, + [setSearchState] + ); + + const addFilter = useCallback( + ({ + fieldName, + operation, + value, + }: { + fieldName: string; + value: string; + operation: '+' | '-'; + }) => { + if (dataView) { + const newFilters = generateFilters( + filterManagerService, + fieldName, + value, + operation, + dataView + ); + setSearchState({ type: 'SET_FILTERS', filters: [...newFilters, ...searchState.filters] }); + } + }, + [dataView, filterManagerService, searchState.filters, setSearchState] + ); + + const stringifiedEsQuery = useMemo(() => { + if (dataView) { + return JSON.stringify( + buildEsQuery(dataView, searchState.query, [ + ...searchState.panelFilters, + ...searchState.filters, + ]) + ); + } + }, [dataView, searchState.panelFilters, searchState.filters, searchState.query]); + + return { + isControlPanelsInitiated, + setIsControlPanelsInitiated, + dataView, + refreshSubject$, + searchState, + addFilter, + stringifiedEsQuery, + onQueryChange, + onPanelFiltersChange, + onFiltersChange, + }; +} + +const UnifiedSearch = createContainer(useUnifiedSearch); +export const [UnifiedSearchProvider, useUnifiedSearchContext] = UnifiedSearch; diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_url.ts b/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_url.ts new file mode 100644 index 000000000000..17cf0ef0d959 --- /dev/null +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_unified_search_url.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FilterStateStore } from '@kbn/es-query'; +import { useUrlState } from '@kbn/observability-shared-plugin/public'; +import { enumeration } from '@kbn/securitysolution-io-ts-types'; +import { fold } from 'fp-ts/lib/Either'; +import { constant, identity } from 'fp-ts/lib/function'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as t from 'io-ts'; +import { useReducer } from 'react'; +import deepEqual from 'fast-deep-equal'; + +const FilterRT = t.intersection([ + t.type({ + meta: t.partial({ + alias: t.union([t.null, t.string]), + disabled: t.boolean, + negate: t.boolean, + controlledBy: t.string, + group: t.string, + index: t.string, + isMultiIndex: t.boolean, + type: t.string, + key: t.string, + params: t.any, + value: t.any, + }), + }), + t.partial({ + query: t.record(t.string, t.any), + $state: t.type({ + store: enumeration('FilterStateStore', FilterStateStore), + }), + }), +]); +const FiltersRT = t.array(FilterRT); + +const QueryStateRT = t.type({ + language: t.string, + query: t.union([t.string, t.record(t.string, t.any)]), +}); + +const SearchStateRT = t.type({ + panelFilters: FiltersRT, + filters: FiltersRT, + query: QueryStateRT, +}); + +const encodeUrlState = SearchStateRT.encode; +const decodeUrlState = (value: unknown) => { + return pipe(SearchStateRT.decode(value), fold(constant(undefined), identity)); +}; + +type SearchState = t.TypeOf; + +const INITIAL_VALUE: SearchState = { + query: { language: 'kuery', query: '' }, + panelFilters: [], + filters: [], +}; + +export type StateAction = + | { type: 'SET_FILTERS'; filters: SearchState['filters'] } + | { type: 'SET_QUERY'; query: SearchState['query'] } + | { type: 'SET_PANEL_FILTERS'; panelFilters: SearchState['panelFilters'] }; + +const reducer = (state: SearchState, action: StateAction): SearchState => { + switch (action.type) { + case 'SET_FILTERS': + return { ...state, filters: action.filters }; + case 'SET_QUERY': + return { ...state, query: action.query }; + case 'SET_PANEL_FILTERS': + return { ...state, panelFilters: action.panelFilters }; + default: + return state; + } +}; + +export function useUnifiedSearchUrl() { + const [urlState, setUrlState] = useUrlState({ + defaultState: INITIAL_VALUE, + decodeUrlState, + encodeUrlState, + urlStateKey: '_a', + writeDefaultState: true, + }); + + const [searchState, setSearchState] = useReducer(reducer, urlState); + + if (!deepEqual(searchState, urlState)) { + setUrlState(searchState); + } + + return { searchState, setSearchState }; +} diff --git a/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx b/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx index 03f8b6475175..f34df1a3c8b3 100644 --- a/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/pages/inventory_page/index.tsx @@ -4,36 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useEffect } from 'react'; -import { useInventoryParams } from '../../hooks/use_inventory_params'; -import { useInventorySearchBarContext } from '../../context/inventory_search_bar_context_provider'; -import { useInventoryRouter } from '../../hooks/use_inventory_router'; -import { UnifiedInventory } from '../../components/grouped_inventory/unified_inventory'; +import React from 'react'; import { GroupedInventory } from '../../components/grouped_inventory'; +import { UnifiedInventory } from '../../components/unified_inventory'; +import { useInventoryParams } from '../../hooks/use_inventory_params'; export function InventoryPage() { - const { searchBarContentSubject$ } = useInventorySearchBarContext(); - const inventoryRoute = useInventoryRouter(); const { query } = useInventoryParams('/'); - - useEffect(() => { - const searchBarContentSubscription = searchBarContentSubject$.subscribe( - ({ ...queryParams }) => { - const { pagination: _, ...rest } = query; - - inventoryRoute.push('/', { - path: {}, - query: { ...rest, ...queryParams }, - }); - } - ); - return () => { - searchBarContentSubscription.unsubscribe(); - }; - // If query has updated, the inventoryRoute state is also updated - // as well, so we only need to track changes on query. - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [query, searchBarContentSubject$]); - return query.view === 'unified' ? : ; } diff --git a/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx b/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx index 36a15c5ae542..bf5f8324aab2 100644 --- a/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/routes/config.tsx @@ -9,12 +9,7 @@ import * as t from 'io-ts'; import React from 'react'; import { InventoryPageTemplate } from '../components/inventory_page_template'; import { InventoryPage } from '../pages/inventory_page'; -import { - defaultEntitySortField, - entityTypesRt, - entityColumnIdsRt, - entityViewRt, -} from '../../common/entities'; +import { defaultEntitySortField, entityColumnIdsRt, entityViewRt } from '../../common/entities'; /** * The array of route definitions to be used when the application @@ -34,10 +29,10 @@ const inventoryRoutes = { sortDirection: t.union([t.literal('asc'), t.literal('desc')]), }), t.partial({ - entityTypes: entityTypesRt, - kuery: t.string, view: entityViewRt, pagination: t.string, + _a: t.string, + controlPanels: t.string, }), ]), }), diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts index 54d20ea324b1..c4c238fba5f8 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_client.ts @@ -14,7 +14,6 @@ import { type EntityInventoryViewedParams, type EntityInventorySearchQuerySubmittedParams, type EntityViewClickedParams, - type EntityInventoryEntityTypeFilteredParams, } from './types'; export class TelemetryClient implements ITelemetryClient { @@ -34,12 +33,6 @@ export class TelemetryClient implements ITelemetryClient { this.analytics.reportEvent(TelemetryEventTypes.ENTITY_INVENTORY_SEARCH_QUERY_SUBMITTED, params); }; - public reportEntityInventoryEntityTypeFiltered = ( - params: EntityInventoryEntityTypeFilteredParams - ) => { - this.analytics.reportEvent(TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, params); - }; - public reportEntityViewClicked = (params: EntityViewClickedParams) => { this.analytics.reportEvent(TelemetryEventTypes.ENTITY_VIEW_CLICKED, params); }; diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts index d61a90f7d30a..ec2623fe2a2c 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_events.ts @@ -49,15 +49,6 @@ const searchQuerySubmittedEventType: TelemetryEvent = { }, }, }, - entity_types: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: 'Entity types used in the search.', - }, - }, - }, action: { type: 'keyword', _meta: { @@ -67,30 +58,6 @@ const searchQuerySubmittedEventType: TelemetryEvent = { }, }; -const entityInventoryEntityTypeFilteredEventType: TelemetryEvent = { - eventType: TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, - schema: { - entity_types: { - type: 'array', - items: { - type: 'keyword', - _meta: { - description: 'Entity types used in the filter.', - }, - }, - }, - kuery_fields: { - type: 'array', - items: { - type: 'text', - _meta: { - description: 'Kuery fields used in the filter.', - }, - }, - }, - }, -}; - const entityViewClickedEventType: TelemetryEvent = { eventType: TelemetryEventTypes.ENTITY_VIEW_CLICKED, schema: { @@ -113,6 +80,5 @@ export const inventoryTelemetryEventBasedTypes = [ inventoryAddDataEventType, entityInventoryViewedEventType, searchQuerySubmittedEventType, - entityInventoryEntityTypeFilteredEventType, entityViewClickedEventType, ]; diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts index 415cf0e7d440..639b771788f5 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/telemetry_service.test.ts @@ -13,7 +13,6 @@ import { type EntityViewClickedParams, type EntityInventorySearchQuerySubmittedParams, TelemetryEventTypes, - type EntityInventoryEntityTypeFilteredParams, } from './types'; describe('TelemetryService', () => { @@ -115,7 +114,6 @@ describe('TelemetryService', () => { const params: EntityInventorySearchQuerySubmittedParams = { kuery_fields: ['_index'], action: 'submit', - entity_types: ['container'], }; telemetry.reportEntityInventorySearchQuerySubmitted(params); @@ -128,26 +126,6 @@ describe('TelemetryService', () => { }); }); - describe('#reportEntityInventoryEntityTypeFiltered', () => { - it('should report entity type filtered with properties', async () => { - const setupParams = getSetupParams(); - service.setup(setupParams); - const telemetry = service.start(); - const params: EntityInventoryEntityTypeFilteredParams = { - kuery_fields: ['_index'], - entity_types: ['container'], - }; - - telemetry.reportEntityInventoryEntityTypeFiltered(params); - - expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); - expect(setupParams.analytics.reportEvent).toHaveBeenCalledWith( - TelemetryEventTypes.ENTITY_INVENTORY_ENTITY_TYPE_FILTERED, - params - ); - }); - }); - describe('#reportEntityViewClicked', () => { it('should report entity view clicked with properties', async () => { const setupParams = getSetupParams(); diff --git a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts index 0e52d115d459..0d56f44c2c2f 100644 --- a/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts +++ b/x-pack/plugins/observability_solution/inventory/public/services/telemetry/types.ts @@ -27,15 +27,9 @@ export interface EntityInventoryViewedParams { export interface EntityInventorySearchQuerySubmittedParams { kuery_fields: string[]; - entity_types: string[]; action: 'submit' | 'refresh'; } -export interface EntityInventoryEntityTypeFilteredParams { - kuery_fields: string[]; - entity_types: string[]; -} - export interface EntityViewClickedParams { entity_type: string; view_type: 'detail' | 'flyout'; @@ -45,7 +39,6 @@ export type TelemetryEventParams = | InventoryAddDataParams | EntityInventoryViewedParams | EntityInventorySearchQuerySubmittedParams - | EntityInventoryEntityTypeFilteredParams | EntityViewClickedParams; export interface ITelemetryClient { @@ -54,7 +47,6 @@ export interface ITelemetryClient { reportEntityInventorySearchQuerySubmitted( params: EntityInventorySearchQuerySubmittedParams ): void; - reportEntityInventoryEntityTypeFiltered(params: EntityInventoryEntityTypeFilteredParams): void; reportEntityViewClicked(params: EntityViewClickedParams): void; } diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts index b61f245f1aaf..8c72e18bc074 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_entity_groups.ts @@ -5,11 +5,9 @@ * 2.0. */ +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client'; -import { kqlQuery } from '@kbn/observability-utils/es/queries/kql_query'; import { esqlResultToPlainObjects } from '@kbn/observability-utils/es/utils/esql_result_to_plain_objects'; -import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; -import { ScalarValue } from '@elastic/elasticsearch/lib/api/types'; import { ENTITIES_LATEST_ALIAS, type EntityGroup, @@ -20,38 +18,23 @@ import { getBuiltinEntityDefinitionIdESQLWhereClause } from './query_helper'; export async function getEntityGroupsBy({ inventoryEsClient, field, - kuery, - entityTypes, + esQuery, }: { inventoryEsClient: ObservabilityElasticsearchClient; field: string; - kuery?: string; - entityTypes?: string[]; + esQuery?: QueryDslQueryContainer; }) { const from = `FROM ${ENTITIES_LATEST_ALIAS}`; const where = [getBuiltinEntityDefinitionIdESQLWhereClause()]; - const params: ScalarValue[] = []; - if (entityTypes) { - where.push(`WHERE ${ENTITY_TYPE} IN (${entityTypes.map(() => '?').join()})`); - params.push(...entityTypes); - } - - // STATS doesn't support parameterisation. const group = `STATS count = COUNT(*) by ${field}`; const sort = `SORT ${field} asc`; - // LIMIT doesn't support parameterisation. const limit = `LIMIT ${MAX_NUMBER_OF_ENTITIES}`; const query = [from, ...where, group, sort, limit].join(' | '); const groups = await inventoryEsClient.esql('get_entities_groups', { query, - filter: { - bool: { - filter: kqlQuery(kuery), - }, - }, - params, + filter: esQuery, }); return esqlResultToPlainObjects(groups); diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts index c95a488ad49d..402d11720a9d 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities.ts @@ -5,11 +5,10 @@ * 2.0. */ +import type { QueryDslQueryContainer, ScalarValue } from '@elastic/elasticsearch/lib/api/types'; +import { ENTITY_LAST_SEEN, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client'; -import { kqlQuery } from '@kbn/observability-utils/es/queries/kql_query'; import { esqlResultToPlainObjects } from '@kbn/observability-utils/es/utils/esql_result_to_plain_objects'; -import { ENTITY_LAST_SEEN, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; -import type { ScalarValue } from '@elastic/elasticsearch/lib/api/types'; import { ENTITIES_LATEST_ALIAS, MAX_NUMBER_OF_ENTITIES, @@ -22,14 +21,14 @@ export async function getLatestEntities({ inventoryEsClient, sortDirection, sortField, + esQuery, entityTypes, - kuery, }: { inventoryEsClient: ObservabilityElasticsearchClient; sortDirection: 'asc' | 'desc'; sortField: EntityColumnIds; + esQuery?: QueryDslQueryContainer; entityTypes?: string[]; - kuery?: string; }) { // alertsCount doesn't exist in entities index. Ignore it and sort by entity.lastSeenTimestamp by default. const entitiesSortField = sortField === 'alertsCount' ? ENTITY_LAST_SEEN : sortField; @@ -50,11 +49,7 @@ export async function getLatestEntities({ const latestEntitiesEsqlResponse = await inventoryEsClient.esql('get_latest_entities', { query, - filter: { - bool: { - filter: [...kqlQuery(kuery)], - }, - }, + filter: esQuery, params, }); diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities_alerts.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities_alerts.ts index e969f1d537e9..8126c69de692 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities_alerts.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/get_latest_entities_alerts.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { kqlQuery, termQuery } from '@kbn/observability-plugin/server'; -import { ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; +import { termQuery } from '@kbn/observability-plugin/server'; import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common'; +import { ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; import { AlertsClient } from '../../lib/create_alerts_client.ts/create_alerts_client'; import { getGroupByTermsAgg } from './get_group_by_terms_agg'; import { IdentityFieldsPerEntityType } from './get_identity_fields_per_entity_type'; @@ -21,11 +21,9 @@ type EntityTypeBucketsAggregation = Record; export async function getLatestEntitiesAlerts({ alertsClient, - kuery, identityFieldsPerEntityType, }: { alertsClient: AlertsClient; - kuery?: string; identityFieldsPerEntityType: IdentityFieldsPerEntityType; }): Promise> { if (identityFieldsPerEntityType.size === 0) { @@ -37,7 +35,7 @@ export async function getLatestEntitiesAlerts({ track_total_hits: false, query: { bool: { - filter: [...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), ...kqlQuery(kuery)], + filter: termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), }, }, }; diff --git a/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts b/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts index 88d6cb68ee21..ae99713375b1 100644 --- a/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts +++ b/x-pack/plugins/observability_solution/inventory/server/routes/entities/route.ts @@ -47,8 +47,8 @@ export const listLatestEntitiesRoute = createInventoryServerRoute({ sortDirection: t.union([t.literal('asc'), t.literal('desc')]), }), t.partial({ + esQuery: jsonRt.pipe(t.UnknownRecord), entityTypes: jsonRt.pipe(t.array(t.string)), - kuery: t.string, }), ]), }), @@ -69,7 +69,7 @@ export const listLatestEntitiesRoute = createInventoryServerRoute({ plugin: `@kbn/${INVENTORY_APP_ID}-plugin`, }); - const { sortDirection, sortField, entityTypes, kuery } = params.query; + const { sortDirection, sortField, esQuery, entityTypes } = params.query; const [alertsClient, latestEntities] = await Promise.all([ createAlertsClient({ plugins, request }), @@ -77,8 +77,8 @@ export const listLatestEntitiesRoute = createInventoryServerRoute({ inventoryEsClient, sortDirection, sortField, + esQuery, entityTypes, - kuery, }), ]); @@ -87,7 +87,6 @@ export const listLatestEntitiesRoute = createInventoryServerRoute({ const alerts = await getLatestEntitiesAlerts({ identityFieldsPerEntityType, alertsClient, - kuery, }); const joined = joinByKey( @@ -114,8 +113,7 @@ export const groupEntitiesByRoute = createInventoryServerRoute({ t.type({ path: t.type({ field: t.literal(ENTITY_TYPE) }) }), t.partial({ query: t.partial({ - kuery: t.string, - entityTypes: jsonRt.pipe(t.array(t.string)), + esQuery: jsonRt.pipe(t.UnknownRecord), }), }), ]), @@ -131,13 +129,12 @@ export const groupEntitiesByRoute = createInventoryServerRoute({ }); const { field } = params.path; - const { kuery, entityTypes } = params.query ?? {}; + const { esQuery } = params.query ?? {}; const groups = await getEntityGroupsBy({ inventoryEsClient, field, - kuery, - entityTypes, + esQuery, }); const entitiesCount = groups.reduce((acc, group) => acc + group.count, 0); diff --git a/x-pack/plugins/observability_solution/inventory/tsconfig.json b/x-pack/plugins/observability_solution/inventory/tsconfig.json index d41ef612c957..5cb95e8ac6de 100644 --- a/x-pack/plugins/observability_solution/inventory/tsconfig.json +++ b/x-pack/plugins/observability_solution/inventory/tsconfig.json @@ -56,6 +56,8 @@ "@kbn/zod", "@kbn/dashboard-plugin", "@kbn/deeplinks-analytics", + "@kbn/controls-plugin", + "@kbn/securitysolution-io-ts-types", "@kbn/react-hooks" ] } diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx index a9c45828a755..f82e9436fe5c 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx @@ -16,13 +16,13 @@ import { noop } from 'lodash'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; +import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public'; import { LogEntryCursor } from '../../../common/log_entry'; import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; import { useLogView } from '../../hooks/use_log_view'; import { LogViewsClient } from '../../services/log_views'; import { LogColumnRenderConfiguration } from '../../utils/log_column_render_configuration'; -import { useKibanaQuerySettings } from '../../utils/use_kibana_query_settings'; import { useLogEntryFlyout } from '../logging/log_entry_flyout'; import { ScrollableLogTextStreamView, VisibleInterval } from '../logging/log_text_stream'; import { LogStreamErrorBoundary } from './log_stream_error_boundary'; diff --git a/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts b/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts deleted file mode 100644 index 521cd0142303..000000000000 --- a/x-pack/plugins/observability_solution/logs_shared/public/utils/use_kibana_query_settings.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { EsQueryConfig } from '@kbn/es-query'; -import { SerializableRecord } from '@kbn/utility-types'; -import { useMemo } from 'react'; -import { UI_SETTINGS } from '@kbn/data-plugin/public'; -import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; - -export const useKibanaQuerySettings = (): EsQueryConfig => { - const [allowLeadingWildcards] = useUiSetting$(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS); - const [queryStringOptions] = useUiSetting$(UI_SETTINGS.QUERY_STRING_OPTIONS); - const [dateFormatTZ] = useUiSetting$(UI_SETTINGS.DATEFORMAT_TZ); - const [ignoreFilterIfFieldNotInIndex] = useUiSetting$( - UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX - ); - - return useMemo( - () => ({ - allowLeadingWildcards, - queryStringOptions, - dateFormatTZ, - ignoreFilterIfFieldNotInIndex, - }), - [allowLeadingWildcards, dateFormatTZ, ignoreFilterIfFieldNotInIndex, queryStringOptions] - ); -}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_control_panels_url_state.ts similarity index 63% rename from x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts rename to x-pack/plugins/observability_solution/observability_shared/public/hooks/use_control_panels_url_state.ts index 19c0580c73f8..0ade125dcf81 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_control_panels_url_state.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_control_panels_url_state.ts @@ -12,85 +12,49 @@ import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import type { DataView } from '@kbn/data-views-plugin/public'; import { useMemo } from 'react'; -import { useUrlState } from '../../../../hooks/use_url_state'; +import { useUrlState } from './use_url_state'; -const HOST_FILTERS_URL_STATE_KEY = 'controlPanels'; +const CONTROL_PANELS_URL_KEY = 'controlPanels'; -export const availableControlsPanels = { - HOST_OS_NAME: 'host.os.name', - CLOUD_PROVIDER: 'cloud.provider', - SERVICE_NAME: 'service.name', -}; - -const controlPanelConfigs: ControlPanels = { - [availableControlsPanels.HOST_OS_NAME]: { - order: 0, - width: 'medium', - grow: false, - type: 'optionsListControl', - fieldName: availableControlsPanels.HOST_OS_NAME, - title: 'Operating System', - }, - [availableControlsPanels.CLOUD_PROVIDER]: { - order: 1, - width: 'medium', - grow: false, - type: 'optionsListControl', - fieldName: availableControlsPanels.CLOUD_PROVIDER, - title: 'Cloud Provider', - }, - [availableControlsPanels.SERVICE_NAME]: { - order: 2, - width: 'medium', - grow: false, - type: 'optionsListControl', - fieldName: availableControlsPanels.SERVICE_NAME, - title: 'Service Name', - }, -}; +const PanelRT = rt.intersection([ + rt.type({ + order: rt.number, + type: rt.string, + }), + rt.partial({ + width: rt.union([rt.literal('medium'), rt.literal('small'), rt.literal('large')]), + grow: rt.boolean, + dataViewId: rt.string, + fieldName: rt.string, + title: rt.union([rt.string, rt.undefined]), + selectedOptions: rt.array(rt.string), + }), +]); -const availableControlPanelFields = Object.values(availableControlsPanels); +const ControlPanelRT = rt.record(rt.string, PanelRT); +export type ControlPanels = rt.TypeOf; -export const useControlPanels = ( +const getVisibleControlPanels = ( + availableControlPanelFields: string[], dataView: DataView | undefined -): [ControlPanels, (state: ControlPanels) => void] => { - const defaultState = useMemo(() => getVisibleControlPanelsConfig(dataView), [dataView]); - - const [controlPanels, setControlPanels] = useUrlState({ - defaultState, - decodeUrlState, - encodeUrlState, - urlStateKey: HOST_FILTERS_URL_STATE_KEY, - }); - - /** - * Configure the control panels as - * 1. Available fields from the data view - * 2. Existing filters from the URL parameter (not colliding with allowed fields from data view) - * 3. Enhanced with dataView.id - */ - const controlsPanelsWithId = dataView - ? mergeDefaultPanelsWithUrlConfig(dataView, controlPanels) - : ({} as ControlPanels); - - return [controlsPanelsWithId, setControlPanels]; -}; - -/** - * Utils - */ -const getVisibleControlPanels = (dataView: DataView | undefined) => { +) => { return availableControlPanelFields.filter( (panelKey) => dataView?.fields.getByName(panelKey) !== undefined ); }; -const getVisibleControlPanelsConfig = (dataView: DataView | undefined) => { - return getVisibleControlPanels(dataView).reduce((panelsMap, panelKey) => { - const config = controlPanelConfigs[panelKey]; - - return { ...panelsMap, [panelKey]: config }; - }, {} as ControlPanels); +const getVisibleControlPanelsConfig = ( + controlPanelConfigs: ControlPanels, + dataView: DataView | undefined +) => { + return getVisibleControlPanels(Object.keys(controlPanelConfigs), dataView).reduce( + (panelsMap, panelKey) => { + const config = controlPanelConfigs[panelKey]; + + return { ...panelsMap, [panelKey]: config }; + }, + {} as ControlPanels + ); }; const addDataViewIdToControlPanels = (controlPanels: ControlPanels, dataViewId: string = '') => { @@ -115,9 +79,13 @@ const cleanControlPanels = (controlPanels: ControlPanels) => { }, {}); }; -const mergeDefaultPanelsWithUrlConfig = (dataView: DataView, urlPanels: ControlPanels = {}) => { +const mergeDefaultPanelsWithUrlConfig = ( + dataView: DataView, + urlPanels: ControlPanels = {}, + controlPanelConfigs: ControlPanels +) => { // Get default panel configs from existing fields in data view - const visiblePanels = getVisibleControlPanelsConfig(dataView); + const visiblePanels = getVisibleControlPanelsConfig(controlPanelConfigs, dataView); // Get list of panel which can be overridden to avoid merging additional config from url const existingKeys = Object.keys(visiblePanels); @@ -130,25 +98,6 @@ const mergeDefaultPanelsWithUrlConfig = (dataView: DataView, urlPanels: ControlP ); }; -const PanelRT = rt.intersection([ - rt.type({ - order: rt.number, - type: rt.string, - }), - rt.partial({ - width: rt.union([rt.literal('medium'), rt.literal('small'), rt.literal('large')]), - grow: rt.boolean, - dataViewId: rt.string, - fieldName: rt.string, - title: rt.union([rt.string, rt.undefined]), - selectedOptions: rt.array(rt.string), - }), -]); - -const ControlPanelRT = rt.record(rt.string, PanelRT); - -type ControlPanels = rt.TypeOf; - const encodeUrlState = (value: ControlPanels) => { if (value) { // Remove the dataView.id on update to make the control panels portable between data views @@ -163,3 +112,32 @@ const decodeUrlState = (value: unknown) => { return pipe(ControlPanelRT.decode(value), fold(constant({}), identity)); } }; + +export const useControlPanels = ( + controlPanelConfigs: ControlPanels, + dataView: DataView | undefined +): [ControlPanels, (state: ControlPanels) => void] => { + const defaultState = useMemo( + () => getVisibleControlPanelsConfig(controlPanelConfigs, dataView), + [controlPanelConfigs, dataView] + ); + + const [controlPanels, setControlPanels] = useUrlState({ + defaultState, + decodeUrlState, + encodeUrlState, + urlStateKey: CONTROL_PANELS_URL_KEY, + }); + + /** + * Configure the control panels as + * 1. Available fields from the data view + * 2. Existing filters from the URL parameter (not colliding with allowed fields from data view) + * 3. Enhanced with dataView.id + */ + const controlsPanelsWithId = dataView + ? mergeDefaultPanelsWithUrlConfig(dataView, controlPanels, controlPanelConfigs) + : ({} as ControlPanels); + + return [controlsPanelsWithId, setControlPanels]; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_kibana_query_settings.ts b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_kibana_query_settings.ts similarity index 100% rename from x-pack/plugins/observability_solution/infra/public/hooks/use_kibana_query_settings.ts rename to x-pack/plugins/observability_solution/observability_shared/public/hooks/use_kibana_query_settings.ts diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_url_state.ts b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_url_state.ts similarity index 81% rename from x-pack/plugins/observability_solution/infra/public/hooks/use_url_state.ts rename to x-pack/plugins/observability_solution/observability_shared/public/hooks/use_url_state.ts index a581e6536e48..ac18fbe41300 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_url_state.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_url_state.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { parse } from 'query-string'; +import { parse, stringify } from 'query-string'; import { Location } from 'history'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { decode, RisonValue } from '@kbn/rison'; +import { decode, encode, RisonValue } from '@kbn/rison'; import { useHistory } from 'react-router-dom'; -import { replaceStateKeyInQueryString } from '../../common/url_state_storage_service'; +import { url } from '@kbn/kibana-utils-plugin/common'; export const useUrlState = ({ defaultState, @@ -94,7 +94,7 @@ export const useUrlState = ({ return [state, setState] as [typeof state, typeof setState]; }; -const decodeRisonUrlState = (value: string | undefined | null): RisonValue | undefined => { +export const decodeRisonUrlState = (value: string | undefined | null): RisonValue | undefined => { try { return value ? decode(value) : undefined; } catch (error) { @@ -124,3 +124,19 @@ const replaceQueryStringInLocation = (location: Location, queryString: string): }; } }; + +const encodeRisonUrlState = (state: any) => encode(state); + +const replaceStateKeyInQueryString = + (stateKey: string, urlState: UrlState | undefined) => + (queryString: string) => { + const previousQueryValues = parse(queryString, { sort: false }); + const newValue = + typeof urlState === 'undefined' + ? previousQueryValues + : { + ...previousQueryValues, + [stateKey]: encodeRisonUrlState(urlState), + }; + return stringify(url.encodeQuery(newValue), { sort: false, encode: false }); + }; diff --git a/x-pack/plugins/observability_solution/observability_shared/public/index.ts b/x-pack/plugins/observability_solution/observability_shared/public/index.ts index d732a669e45b..bc9f2c452cc8 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/index.ts @@ -104,3 +104,7 @@ export { BottomBarActions } from './components/bottom_bar_actions/bottom_bar_act export { FieldValueSelection, FieldValueSuggestions } from './components'; export { AddDataPanel, type AddDataPanelProps } from './components/add_data_panel'; + +export { useUrlState } from './hooks/use_url_state'; +export { type ControlPanels, useControlPanels } from './hooks/use_control_panels_url_state'; +export { useKibanaQuerySettings } from './hooks/use_kibana_query_settings'; diff --git a/x-pack/plugins/observability_solution/observability_shared/tsconfig.json b/x-pack/plugins/observability_solution/observability_shared/tsconfig.json index f68649c85cea..f7b8a7ff6c57 100644 --- a/x-pack/plugins/observability_solution/observability_shared/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_shared/tsconfig.json @@ -45,6 +45,7 @@ "@kbn/rule-data-utils", "@kbn/es-query", "@kbn/serverless", + "@kbn/data-views-plugin", ], "exclude": ["target/**/*", ".storybook/**/*.js"] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index c2d5da848212..0cff8e466d31 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -26269,8 +26269,6 @@ "xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "Horodatage des dernières données reçues pour l'entité (entity.lastSeenTimestamp)", "xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "Type", "xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "Type d'entité (entity.type)", - "xpack.inventory.entityTypesControls.euiComboBox.accessibleScreenReaderLabel": "Filtre des types d'entités", - "xpack.inventory.entityTypesControls.euiComboBox.placeHolderLabel": "Types", "xpack.inventory.featureRegistry.inventoryFeatureName": "Inventory", "xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "Alertes actives", "xpack.inventory.inventoryLinkTitle": "Inventory", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ef3bd55d92ef..17fb0b2ff933 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26240,8 +26240,6 @@ "xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "エンティティで最後に受信したデータのタイムスタンプ(entity.lastSeenTimestamp)", "xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "型", "xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "エンティティのタイプ(entity.type)", - "xpack.inventory.entityTypesControls.euiComboBox.accessibleScreenReaderLabel": "エンティティタイプフィルター", - "xpack.inventory.entityTypesControls.euiComboBox.placeHolderLabel": "タイプ", "xpack.inventory.featureRegistry.inventoryFeatureName": "インベントリ", "xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "アクティブアラート", "xpack.inventory.inventoryLinkTitle": "インベントリ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6543250dfb98..6ea71271a685 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25767,8 +25767,6 @@ "xpack.inventory.entitiesGrid.euiDataGrid.lastSeenTooltip": "上次接收的实体数据的时间戳 (entity.lastSeenTimestamp)", "xpack.inventory.entitiesGrid.euiDataGrid.typeLabel": "类型", "xpack.inventory.entitiesGrid.euiDataGrid.typeTooltip": "实体的类型 (entity.type)", - "xpack.inventory.entityTypesControls.euiComboBox.accessibleScreenReaderLabel": "实体类型筛选", - "xpack.inventory.entityTypesControls.euiComboBox.placeHolderLabel": "类型", "xpack.inventory.featureRegistry.inventoryFeatureName": "库存", "xpack.inventory.home.serviceAlertsTable.tooltip.activeAlertsExplanation": "活动告警", "xpack.inventory.inventoryLinkTitle": "库存", From 84d79e1442620328bfa85f0b0caec2ca818f2475 Mon Sep 17 00:00:00 2001 From: "elastic-renovate-prod[bot]" <174716857+elastic-renovate-prod[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 11:41:15 +0100 Subject: [PATCH 044/100] Update dependency @elastic/elasticsearch to ^8.15.2 (main) (#199705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: elastic-renovate-prod[bot] <174716857+elastic-renovate-prod[bot]@users.noreply.github.com> Co-authored-by: Alejandro Fernández Haro --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ef0d751ff752..87905c955d2d 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "@elastic/datemath": "5.0.3", "@elastic/ebt": "^1.1.1", "@elastic/ecs": "^8.11.1", - "@elastic/elasticsearch": "^8.15.1", + "@elastic/elasticsearch": "^8.15.2", "@elastic/ems-client": "8.5.3", "@elastic/eui": "97.3.1", "@elastic/filesaver": "1.1.2", diff --git a/yarn.lock b/yarn.lock index e5e3c6b0020b..ca28b48ef37c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1721,10 +1721,10 @@ "@elastic/transport" "^8.3.1" tslib "^2.4.0" -"@elastic/elasticsearch@^8.15.1": - version "8.15.1" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.15.1.tgz#ca294ba11ed1514bf87d4a2e253b11f6cefd8552" - integrity sha512-L3YzSaxrasMMGtcxnktiUDjS5f177L0zpHsBH+jL0LgPhdMk9xN/VKrAaYzvri86IlV5IbveA0ANV6o/BDUmhQ== +"@elastic/elasticsearch@^8.15.2": + version "8.15.2" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.15.2.tgz#024e3617b0d1db6425b369a7176d3514598e4c9e" + integrity sha512-DTvjK4bs4WkgdQkXZJx2eVcwKzF1it/PyGT3rl1ReUNIWbkzMEKksiDQK3wY1U3WHT4zTuWQi4GrDOC1w5ej8Q== dependencies: "@elastic/transport" "^8.8.1" tslib "^2.4.0" From 803738fa0c8fea3cdfc4760c7b3dd667637de724 Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Tue, 12 Nov 2024 11:41:34 +0100 Subject: [PATCH 045/100] [APM] Migrate `/data_view` to deployment agnostic test (#199296) ## Summary Closes https://github.com/elastic/kibana/issues/198965 Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `data_view` test folder to Deployment-agnostic testing strategy. ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` ## Checks - [x] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --- .../apis/observability/apm/data_view/index.ts | 14 + .../apm/data_view/static.spec.ts | 291 ++++++++++++++++++ .../apis/observability/apm/index.ts | 1 + .../tests/data_view/static.spec.ts | 268 ---------------- 4 files changed, 306 insertions(+), 268 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/index.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/static.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/data_view/static.spec.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/index.ts new file mode 100644 index 000000000000..9412ddca7cbc --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('data_view', () => { + loadTestFile(require.resolve('./static.spec.ts')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/static.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/static.spec.ts new file mode 100644 index 000000000000..2dc71cd599bd --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/data_view/static.spec.ts @@ -0,0 +1,291 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import expect from '@kbn/expect'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; +import request from 'superagent'; +import { getStaticDataViewId } from '@kbn/apm-data-view'; +import { SupertestWithRoleScope } from '../../../../services/role_scoped_supertest'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { + SupertestReturnType, + ApmApiError, +} from '../../../../../../apm_api_integration/common/apm_api_supertest'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const roleScopedSupertest = getService('roleScopedSupertest'); + const synthtraceService = getService('synthtrace'); + const logger = getService('log'); + const dataViewPattern = + 'traces-apm*,apm-*,traces-*.otel-*,logs-apm*,apm-*,logs-*.otel-*,metrics-apm*,apm-*,metrics-*.otel-*'; + + function createDataViewWithWriteUser({ spaceId }: { spaceId: string }) { + return apmApiClient.writeUser({ + endpoint: 'POST /internal/apm/data_view/static', + spaceId, + }); + } + + function createDataViewWithReadUser({ spaceId }: { spaceId: string }) { + return apmApiClient.readUser({ + endpoint: 'POST /internal/apm/data_view/static', + spaceId, + }); + } + + describe('Data view static', () => { + let supertestEditorWithApiKey: SupertestWithRoleScope; + let supertestEditorWithCookieCredentials: SupertestWithRoleScope; + + before(async () => { + supertestEditorWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('editor', { + withInternalHeaders: true, + }); + + supertestEditorWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( + 'editor', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + }); + + after(async () => { + await supertestEditorWithApiKey.destroy(); + }); + + function deleteDataView(spaceId: string) { + return supertestEditorWithApiKey.delete( + `/s/${spaceId}/api/saved_objects/index-pattern/${getStaticDataViewId(spaceId)}?force=true` + ); + } + + function getDataView({ spaceId }: { spaceId: string }) { + const spacePrefix = spaceId !== 'default' ? `/s/${spaceId}` : ''; + return supertestEditorWithApiKey.get( + `${spacePrefix}/api/saved_objects/index-pattern/${getStaticDataViewId(spaceId)}` + ); + } + + function getDataViewSuggestions(field: string) { + return supertestEditorWithCookieCredentials + .post(`/internal/kibana/suggestions/values/${dataViewPattern}`) + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .send({ query: '', field, method: 'terms_agg' }); + } + + describe('no mappings exist', () => { + let response: SupertestReturnType<'POST /internal/apm/data_view/static'>; + before(async () => { + response = await createDataViewWithWriteUser({ spaceId: 'default' }); + }); + + it('does not create data view', async () => { + expect(response.status).to.be(200); + expect(response.body).to.eql({ + created: false, + reason: 'No APM data', + }); + }); + + it('cannot fetch data view', async () => { + const res = await getDataView({ spaceId: 'default' }); + expect(res.status).to.be(404); + expect(res.body.message).to.eql( + 'Saved object [index-pattern/apm_static_data_view_id_default] not found' + ); + }); + }); + + describe('when mappings and APM data exists', () => { + let synthtrace: ApmSynthtraceEsClient; + + before(async () => { + synthtrace = await synthtraceService.createApmSynthtraceEsClient(); + await generateApmData(synthtrace); + }); + + after(async () => { + await synthtrace.clean(); + }); + + afterEach(async () => { + try { + await Promise.all([deleteDataView('default'), deleteDataView('foo')]); + } catch (e) { + logger.error(`Could not delete data views ${e.message}`); + } + }); + + describe('when creating data view with write user', () => { + let response: SupertestReturnType<'POST /internal/apm/data_view/static'>; + + before(async () => { + response = await createDataViewWithWriteUser({ spaceId: 'default' }); + }); + + it('successfully creates the apm data view', async () => { + expect(response.status).to.be(200); + + const dataView = (response.body as { dataView: DataView }).dataView; + + expect(dataView.id).to.be('apm_static_data_view_id_default'); + expect(dataView.name).to.be('APM'); + expect(dataView.title).to.be( + 'traces-apm*,apm-*,traces-*.otel-*,logs-apm*,apm-*,logs-*.otel-*,metrics-apm*,apm-*,metrics-*.otel-*' + ); + }); + }); + + describe('when fetching the data view', () => { + let dataViewResponse: request.Response; + + before(async () => { + await createDataViewWithWriteUser({ spaceId: 'default' }); + dataViewResponse = await getDataView({ spaceId: 'default' }); + }); + + it('return 200', () => { + expect(dataViewResponse.status).to.be(200); + }); + + it('has correct id', () => { + expect(dataViewResponse.body.id).to.be('apm_static_data_view_id_default'); + }); + + it('has correct title', () => { + expect(dataViewResponse.body.attributes.title).to.be(dataViewPattern); + }); + + it('has correct attributes', () => { + expect(dataViewResponse.body.attributes.fieldFormatMap).to.be( + JSON.stringify({ + 'trace.id': { + id: 'url', + params: { + urlTemplate: 'apm/link-to/trace/{{value}}', + labelTemplate: '{{value}}', + }, + }, + 'transaction.id': { + id: 'url', + params: { + urlTemplate: 'apm/link-to/transaction/{{value}}', + labelTemplate: '{{value}}', + }, + }, + 'transaction.duration.us': { + id: 'duration', + params: { + inputFormat: 'microseconds', + outputFormat: 'asMilliseconds', + showSuffix: true, + useShortSuffix: true, + outputPrecision: 2, + includeSpaceWithSuffix: true, + }, + }, + }) + ); + }); + + // this test ensures that the default APM Data View doesn't interfere with suggestions returned in the kuery bar (this has been a problem in the past) + it('can get suggestions for `trace.id`', async () => { + const suggestions = await getDataViewSuggestions('trace.id'); + expect(suggestions.body.length).to.be(10); + }); + }); + + describe('when creating data view via read user', () => { + it('throws an error', async () => { + try { + await createDataViewWithReadUser({ spaceId: 'default' }); + } catch (e) { + const err = e as ApmApiError; + const responseBody = err.res.body; + expect(err.res.status).to.eql(403); + expect(responseBody.statusCode).to.eql(403); + expect(responseBody.error).to.eql('Forbidden'); + expect(responseBody.message).to.eql('Unable to create index-pattern'); + } + }); + }); + + describe('when creating data view twice', () => { + it('returns 200 response with reason, if data view already exists', async () => { + await createDataViewWithWriteUser({ spaceId: 'default' }); + const res = await createDataViewWithWriteUser({ spaceId: 'default' }); + + expect(res.status).to.be(200); + expect(res.body).to.eql({ + created: false, + reason: 'Dataview already exists in the active space and does not need to be updated', + }); + }); + }); + + describe('when creating data view in "default" space', () => { + it('can be retrieved from the "default" space', async () => { + await createDataViewWithWriteUser({ spaceId: 'default' }); + const res = await getDataView({ spaceId: 'default' }); + expect(res.body.id).to.eql('apm_static_data_view_id_default'); + expect(res.body.namespaces).to.eql(['default']); + }); + + it('cannot be retrieved from the "foo" space', async () => { + await createDataViewWithWriteUser({ spaceId: 'default' }); + const res = await getDataView({ spaceId: 'foo' }); + expect(res.body.statusCode).to.be(404); + }); + }); + + describe('when creating data view in "foo" space', () => { + it('can be retrieved from the "foo" space', async () => { + await createDataViewWithWriteUser({ spaceId: 'foo' }); + const res = await getDataView({ spaceId: 'foo' }); + expect(res.body.id).to.eql('apm_static_data_view_id_foo'); + expect(res.body.namespaces).to.eql(['foo']); + }); + + it('cannot be retrieved from the "default" space', async () => { + await createDataViewWithWriteUser({ spaceId: 'foo' }); + const res = await getDataView({ spaceId: 'default' }); + expect(res.body.statusCode).to.be(404); + }); + }); + }); + }); +} + +function generateApmData(synthtrace: ApmSynthtraceEsClient) { + const range = timerange( + new Date('2021-10-01T00:00:00.000Z').getTime(), + new Date('2021-10-01T00:01:00.000Z').getTime() + ); + + const instance = apm + .service({ name: 'my-service', environment: 'production', agentName: 'go' }) + .instance('my-instance'); + + return synthtrace.index([ + range + .interval('1s') + .rate(1) + .generator((timestamp) => + instance + .transaction({ transactionName: 'GET /api' }) + .timestamp(timestamp) + .duration(30) + .success() + ), + ]); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index f1e8fc381a07..22bf6d8aa7f5 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -15,6 +15,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); + loadTestFile(require.resolve('./data_view')); loadTestFile(require.resolve('./correlations')); loadTestFile(require.resolve('./entities')); loadTestFile(require.resolve('./cold_start')); diff --git a/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts b/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts deleted file mode 100644 index 2d6d1b230ef6..000000000000 --- a/x-pack/test/apm_api_integration/tests/data_view/static.spec.ts +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; -import expect from '@kbn/expect'; -import { DataView } from '@kbn/data-views-plugin/common'; -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; -import request from 'superagent'; -import { getStaticDataViewId } from '@kbn/apm-data-view'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { SupertestReturnType, ApmApiError } from '../../common/apm_api_supertest'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const supertest = getService('supertest'); - const synthtrace = getService('apmSynthtraceEsClient'); - const logger = getService('log'); - const dataViewPattern = - 'traces-apm*,apm-*,traces-*.otel-*,logs-apm*,apm-*,logs-*.otel-*,metrics-apm*,apm-*,metrics-*.otel-*'; - - function createDataViewWithWriteUser({ spaceId }: { spaceId: string }) { - return apmApiClient.writeUser({ - endpoint: 'POST /internal/apm/data_view/static', - spaceId, - }); - } - - function createDataViewWithReadUser({ spaceId }: { spaceId: string }) { - return apmApiClient.readUser({ - endpoint: 'POST /internal/apm/data_view/static', - spaceId, - }); - } - - function deleteDataView(spaceId: string) { - return supertest - .delete( - `/s/${spaceId}/api/saved_objects/index-pattern/${getStaticDataViewId(spaceId)}?force=true` - ) - .set('kbn-xsrf', 'foo'); - } - - function getDataView({ spaceId }: { spaceId: string }) { - const spacePrefix = spaceId !== 'default' ? `/s/${spaceId}` : ''; - return supertest.get( - `${spacePrefix}/api/saved_objects/index-pattern/${getStaticDataViewId(spaceId)}` - ); - } - - function getDataViewSuggestions(field: string) { - return supertest - .post(`/internal/kibana/suggestions/values/${dataViewPattern}`) - .set('kbn-xsrf', 'foo') - .set(ELASTIC_HTTP_VERSION_HEADER, '1') - .send({ query: '', field, method: 'terms_agg' }); - } - - registry.when('no mappings exist', { config: 'basic', archives: [] }, () => { - let response: SupertestReturnType<'POST /internal/apm/data_view/static'>; - before(async () => { - response = await createDataViewWithWriteUser({ spaceId: 'default' }); - }); - - it('does not create data view', async () => { - expect(response.status).to.be(200); - expect(response.body).to.eql({ - created: false, - reason: 'No APM data', - }); - }); - - it('cannot fetch data view', async () => { - const res = await getDataView({ spaceId: 'default' }); - expect(res.status).to.be(404); - expect(res.body.message).to.eql( - 'Saved object [index-pattern/apm_static_data_view_id_default] not found' - ); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/177120 - registry.when('mappings and APM data exists', { config: 'basic', archives: [] }, () => { - // eslint-disable-next-line mocha/no-sibling-hooks - before(async () => { - await generateApmData(synthtrace); - }); - - after(async () => { - await synthtrace.clean(); - }); - - afterEach(async () => { - try { - await Promise.all([deleteDataView('default'), deleteDataView('foo')]); - } catch (e) { - logger.error(`Could not delete data views ${e.message}`); - } - }); - - describe('when creating data view with write user', () => { - let response: SupertestReturnType<'POST /internal/apm/data_view/static'>; - - before(async () => { - response = await createDataViewWithWriteUser({ spaceId: 'default' }); - }); - - it('successfully creates the apm data view', async () => { - expect(response.status).to.be(200); - - // @ts-expect-error - const dataView = response.body.dataView as DataView; - - expect(dataView.id).to.be('apm_static_data_view_id_default'); - expect(dataView.name).to.be('APM'); - expect(dataView.title).to.be( - 'traces-apm*,apm-*,traces-*.otel-*,logs-apm*,apm-*,logs-*.otel-*,metrics-apm*,apm-*,metrics-*.otel-*' - ); - }); - }); - - describe('when fetching the data view', () => { - let dataViewResponse: request.Response; - - before(async () => { - await createDataViewWithWriteUser({ spaceId: 'default' }); - dataViewResponse = await getDataView({ spaceId: 'default' }); - }); - - it('return 200', () => { - expect(dataViewResponse.status).to.be(200); - }); - - it('has correct id', () => { - expect(dataViewResponse.body.id).to.be('apm_static_data_view_id_default'); - }); - - it('has correct title', () => { - expect(dataViewResponse.body.attributes.title).to.be(dataViewPattern); - }); - - it('has correct attributes', () => { - expect(dataViewResponse.body.attributes.fieldFormatMap).to.be( - JSON.stringify({ - 'trace.id': { - id: 'url', - params: { - urlTemplate: 'apm/link-to/trace/{{value}}', - labelTemplate: '{{value}}', - }, - }, - 'transaction.id': { - id: 'url', - params: { - urlTemplate: 'apm/link-to/transaction/{{value}}', - labelTemplate: '{{value}}', - }, - }, - 'transaction.duration.us': { - id: 'duration', - params: { - inputFormat: 'microseconds', - outputFormat: 'asMilliseconds', - showSuffix: true, - useShortSuffix: true, - outputPrecision: 2, - includeSpaceWithSuffix: true, - }, - }, - }) - ); - }); - - // this test ensures that the default APM Data View doesn't interfere with suggestions returned in the kuery bar (this has been a problem in the past) - it('can get suggestions for `trace.id`', async () => { - const suggestions = await getDataViewSuggestions('trace.id'); - expect(suggestions.body.length).to.be(10); - }); - }); - - describe('when creating data view via read user', () => { - it('throws an error', async () => { - try { - await createDataViewWithReadUser({ spaceId: 'default' }); - } catch (e) { - const err = e as ApmApiError; - const responseBody = err.res.body; - expect(err.res.status).to.eql(403); - expect(responseBody.statusCode).to.eql(403); - expect(responseBody.error).to.eql('Forbidden'); - expect(responseBody.message).to.eql('Unable to create index-pattern'); - } - }); - }); - - describe('when creating data view twice', () => { - it('returns 200 response with reason, if data view already exists', async () => { - await createDataViewWithWriteUser({ spaceId: 'default' }); - const res = await createDataViewWithWriteUser({ spaceId: 'default' }); - - expect(res.status).to.be(200); - expect(res.body).to.eql({ - created: false, - reason: 'Dataview already exists in the active space and does not need to be updated', - }); - }); - }); - - describe('when creating data view in "default" space', () => { - it('can be retrieved from the "default" space', async () => { - await createDataViewWithWriteUser({ spaceId: 'default' }); - const res = await getDataView({ spaceId: 'default' }); - expect(res.body.id).to.eql('apm_static_data_view_id_default'); - expect(res.body.namespaces).to.eql(['default']); - }); - - it('cannot be retrieved from the "foo" space', async () => { - await createDataViewWithWriteUser({ spaceId: 'default' }); - const res = await getDataView({ spaceId: 'foo' }); - expect(res.body.statusCode).to.be(404); - }); - }); - - describe('when creating data view in "foo" space', () => { - it('can be retrieved from the "foo" space', async () => { - await createDataViewWithWriteUser({ spaceId: 'foo' }); - const res = await getDataView({ spaceId: 'foo' }); - expect(res.body.id).to.eql('apm_static_data_view_id_foo'); - expect(res.body.namespaces).to.eql(['foo']); - }); - - it('cannot be retrieved from the "default" space', async () => { - await createDataViewWithWriteUser({ spaceId: 'foo' }); - const res = await getDataView({ spaceId: 'default' }); - expect(res.body.statusCode).to.be(404); - }); - }); - }); -} - -function generateApmData(synthtrace: ApmSynthtraceEsClient) { - const range = timerange( - new Date('2021-10-01T00:00:00.000Z').getTime(), - new Date('2021-10-01T00:01:00.000Z').getTime() - ); - - const instance = apm - .service({ name: 'my-service', environment: 'production', agentName: 'go' }) - .instance('my-instance'); - - return synthtrace.index([ - range - .interval('1s') - .rate(1) - .generator((timestamp) => - instance - .transaction({ transactionName: 'GET /api' }) - .timestamp(timestamp) - .duration(30) - .success() - ), - ]); -} From 93d7044919fc5e08ddc2fa125407451cc3afb800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Tue, 12 Nov 2024 11:59:52 +0100 Subject: [PATCH 046/100] [Feature Flags] Add APM transaction + better example code (#199671) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/app.tsx | 62 +++++-------- .../public/components/feature_flags_list.tsx | 91 +++++++++++++++++++ examples/feature_flags_example/tsconfig.json | 1 + packages/core/feature-flags/README.mdx | 2 +- .../src/feature_flags_service.ts | 8 ++ 5 files changed, 122 insertions(+), 42 deletions(-) create mode 100644 examples/feature_flags_example/public/components/feature_flags_list.tsx diff --git a/examples/feature_flags_example/public/components/app.tsx b/examples/feature_flags_example/public/components/app.tsx index 432e7dc348ab..97f850b5dd47 100644 --- a/examples/feature_flags_example/public/components/app.tsx +++ b/examples/feature_flags_example/public/components/app.tsx @@ -8,24 +8,15 @@ */ import React from 'react'; -import { - EuiHorizontalRule, - EuiPageTemplate, - EuiTitle, - EuiText, - EuiLink, - EuiListGroup, - EuiListGroupItem, -} from '@elastic/eui'; +import { EuiHorizontalRule, EuiPageTemplate, EuiTitle, EuiText, EuiLink } from '@elastic/eui'; import type { CoreStart, FeatureFlagsStart } from '@kbn/core/public'; -import useObservable from 'react-use/lib/useObservable'; -import { - FeatureFlagExampleBoolean, - FeatureFlagExampleNumber, - FeatureFlagExampleString, -} from '../../common/feature_flags'; import { PLUGIN_NAME } from '../../common'; +import { + FeatureFlagsFullList, + FeatureFlagsReactiveList, + FeatureFlagsStaticList, +} from './feature_flags_list'; interface FeatureFlagsExampleAppDeps { featureFlags: FeatureFlagsStart; @@ -34,16 +25,6 @@ interface FeatureFlagsExampleAppDeps { } export const FeatureFlagsExampleApp = ({ featureFlags }: FeatureFlagsExampleAppDeps) => { - // Fetching the feature flags synchronously - const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false); - const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red'); - const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1); - - // Use React Hooks to observe feature flags changes - const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false)); - const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red')); - const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1)); - return ( <> @@ -67,22 +48,21 @@ export const FeatureFlagsExampleApp = ({ featureFlags }: FeatureFlagsExampleAppD .

- -

- The feature flags are: - - - -

-
- -

- The observed feature flags are: - - - -

-
+

Rendered separately

+

+ Each list are 2 different components, so only the reactive one is re-rendered when the + feature flag is updated and the static one keeps the value until the next refresh. +

+ + + +

Rendered together

+

+ `useObservable` causes a full re-render of the component, updating the{' '} + statically + evaluated flags as well. +

+
diff --git a/examples/feature_flags_example/public/components/feature_flags_list.tsx b/examples/feature_flags_example/public/components/feature_flags_list.tsx new file mode 100644 index 000000000000..73d653529586 --- /dev/null +++ b/examples/feature_flags_example/public/components/feature_flags_list.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { EuiListGroup, EuiListGroupItem } from '@elastic/eui'; +import React from 'react'; +import type { FeatureFlagsStart } from '@kbn/core-feature-flags-browser'; +import useObservable from 'react-use/lib/useObservable'; +import { + FeatureFlagExampleBoolean, + FeatureFlagExampleNumber, + FeatureFlagExampleString, +} from '../../common/feature_flags'; + +export interface FeatureFlagsListProps { + featureFlags: FeatureFlagsStart; +} + +export const FeatureFlagsStaticList = ({ featureFlags }: FeatureFlagsListProps) => { + // Fetching the feature flags synchronously + const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false); + const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red'); + const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1); + + return ( + +

+ The feature flags are: + + + +

+
+ ); +}; + +export const FeatureFlagsReactiveList = ({ featureFlags }: FeatureFlagsListProps) => { + // Use React Hooks to observe feature flags changes + const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false)); + const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red')); + const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1)); + + return ( + +

+ The observed feature flags are: + + + +

+
+ ); +}; + +export const FeatureFlagsFullList = ({ featureFlags }: FeatureFlagsListProps) => { + // Fetching the feature flags synchronously + const bool = featureFlags.getBooleanValue(FeatureFlagExampleBoolean, false); + const str = featureFlags.getStringValue(FeatureFlagExampleString, 'red'); + const num = featureFlags.getNumberValue(FeatureFlagExampleNumber, 1); + + // Use React Hooks to observe feature flags changes + const bool$ = useObservable(featureFlags.getBooleanValue$(FeatureFlagExampleBoolean, false)); + const str$ = useObservable(featureFlags.getStringValue$(FeatureFlagExampleString, 'red')); + const num$ = useObservable(featureFlags.getNumberValue$(FeatureFlagExampleNumber, 1)); + + return ( + <> + +

+ The feature flags are: + + + +

+
+ +

+ The observed feature flags are: + + + +

+
+ + ); +}; diff --git a/examples/feature_flags_example/tsconfig.json b/examples/feature_flags_example/tsconfig.json index bbd68332f3d3..77eb5d09ca85 100644 --- a/examples/feature_flags_example/tsconfig.json +++ b/examples/feature_flags_example/tsconfig.json @@ -20,5 +20,6 @@ "@kbn/core-plugins-server", "@kbn/config-schema", "@kbn/developer-examples-plugin", + "@kbn/core-feature-flags-browser", ] } diff --git a/packages/core/feature-flags/README.mdx b/packages/core/feature-flags/README.mdx index 99e10b72aa68..8a28fdf35932 100644 --- a/packages/core/feature-flags/README.mdx +++ b/packages/core/feature-flags/README.mdx @@ -15,7 +15,7 @@ The service is always enabled, however, it will return the fallback value if a f Kibana only registers a provider when running on Elastic Cloud Hosted/Serverless. And even in those scenarios, we expect that some customers might have network restrictions that might not allow the flags to evaluate. The fallback value must provide a non-broken experience to users. -:warning: Feature Flags are considered dynamic configuration and cannot be used for settings that require restarting Kibana. +⚠️Feature Flags are considered dynamic configuration and cannot be used for settings that require restarting Kibana. One example of invalid use cases are settings used during the `setup` lifecycle of the plugin, such as settings that define if an HTTP route is registered or not. Instead, you should always register the route, and return `404 - Not found` in the route handler if the feature flag returns a _disabled_ state. diff --git a/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts b/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts index e282cc52ddd4..afc32d93aee3 100644 --- a/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts +++ b/packages/core/feature-flags/core-feature-flags-browser-internal/src/feature_flags_service.ts @@ -68,7 +68,15 @@ export class FeatureFlagsService { if (this.isProviderReadyPromise) { throw new Error('A provider has already been set. This API cannot be called twice.'); } + const transaction = apm.startTransaction('set-provider', 'feature-flags'); this.isProviderReadyPromise = OpenFeature.setProviderAndWait(provider); + this.isProviderReadyPromise + .then(() => transaction?.end()) + .catch((err) => { + this.logger.error(err); + apm.captureError(err); + transaction?.end(); + }); }, appendContext: (contextToAppend) => this.appendContext(contextToAppend), }; From fb666aa7653ba2cdc56816ac575475e5bb58e1b4 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 12 Nov 2024 12:01:23 +0100 Subject: [PATCH 047/100] [APM] Migrate apm alerts tests to deployment agnostic (#199097) ## Summary Closes [#198959](https://github.com/elastic/kibana/issues/198959) Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `alerts` test folder to Deployment-agnostic testing strategy. It also fixes a bug when filtering alerts by `kibana.alert.rule.producer`. On serverless ,the producer is `observability`, not `apm` ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - This PR won't pass on MKI while the instance is not created using the `kibana.alert.rule.producer` fix from this PR - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 1 + .../get_apm_service_summary/index.ts | 5 +- .../get_service_group_alerts.ts | 9 +- .../get_service_transaction_groups_alerts.ts | 11 +- .../get_services/get_service_alerts.ts | 11 +- .../apm/agent_explorer/agent_explorer.spec.ts | 2 +- .../latest_agent_versions.spec.ts | 2 +- .../apm}/alerts/error_count_threshold.spec.ts | 163 +++-- .../apm}/alerts/generate_data.ts | 0 .../apm/alerts/helpers/alerting_helper.ts | 96 +++ .../apis/observability/apm/alerts/index.ts | 19 + .../alerts/preview_chart_error_count.spec.ts | 478 +++++++------- .../alerts/preview_chart_error_rate.spec.ts | 607 ++++++++++++++++++ ...preview_chart_transaction_duration.spec.ts | 564 ++++++++++++++++ .../apm}/alerts/transaction_duration.spec.ts | 141 ++-- .../alerts/transaction_error_rate.spec.ts | 138 ++-- .../apis/observability/apm/index.ts | 1 + .../services/alerting_api.ts | 136 +++- .../deployment_agnostic/services/apm_api.ts | 2 + .../tests/alerts/anomaly_alert.spec.ts | 8 +- .../alerts/helpers/alerting_api_helper.ts | 145 +---- .../helpers/cleanup_rule_and_alert_state.ts | 5 +- .../helpers/wait_for_active_apm_alerts.ts | 4 +- .../helpers/wait_for_alerts_for_rule.ts | 5 +- .../wait_for_index_connector_results.ts | 2 +- .../alerts/preview_chart_error_rate.spec.ts | 599 ----------------- ...preview_chart_transaction_duration.spec.ts | 556 ---------------- .../service_group_count.spec.ts | 2 +- .../tests/services/service_alerts.spec.ts | 2 +- .../transactions_groups_alerts.spec.ts | 2 +- 30 files changed, 2046 insertions(+), 1670 deletions(-) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/alerts/error_count_threshold.spec.ts (67%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/alerts/generate_data.ts (100%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/alerts/preview_chart_error_count.spec.ts (50%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_rate.spec.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_transaction_duration.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/alerts/transaction_duration.spec.ts (67%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/alerts/transaction_error_rate.spec.ts (69%) delete mode 100644 x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_rate.spec.ts delete mode 100644 x-pack/test/apm_api_integration/tests/alerts/preview_chart_transaction_duration.spec.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index abc6749d52ea..ef265cf7c569 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1190,6 +1190,7 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/plugins/observability_solution/infra/server/usage @elastic/obs-ux-infra_services-team /x-pack/plugins/observability_solution/infra/server/utils @elastic/obs-ux-infra_services-team /x-pack/test/api_integration/deployment_agnostic/apis/observability/infra @elastic/obs-ux-logs-team +/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm @elastic/obs-ux-logs-team ## Logs UI code exceptions -> @elastic/obs-ux-logs-team /x-pack/test_serverless/functional/page_objects/svl_oblt_onboarding_stream_log_file.ts @elastic/obs-ux-logs-team diff --git a/x-pack/plugins/observability_solution/apm/server/routes/assistant_functions/get_apm_service_summary/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/assistant_functions/get_apm_service_summary/index.ts index d28152127648..5c9d40cc2277 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/assistant_functions/get_apm_service_summary/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/assistant_functions/get_apm_service_summary/index.ts @@ -6,13 +6,14 @@ */ import datemath from '@elastic/datemath'; import { ElasticsearchClient, Logger } from '@kbn/core/server'; -import { rangeQuery, ScopedAnnotationsClient } from '@kbn/observability-plugin/server'; +import { rangeQuery, ScopedAnnotationsClient, termsQuery } from '@kbn/observability-plugin/server'; import { ALERT_RULE_PRODUCER, ALERT_STATUS, ALERT_STATUS_ACTIVE, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import * as t from 'io-ts'; +import { observabilityFeatureId } from '@kbn/observability-shared-plugin/common'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { Environment } from '../../../../common/environment_rt'; import { SERVICE_NAME } from '../../../../common/es_fields/apm'; @@ -139,7 +140,7 @@ export async function getApmServiceSummary({ query: { bool: { filter: [ - ...termQuery(ALERT_RULE_PRODUCER, 'apm'), + ...termsQuery(ALERT_RULE_PRODUCER, 'apm', observabilityFeatureId), ...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), ...rangeQuery(start, end), ...termQuery(SERVICE_NAME, serviceName), diff --git a/x-pack/plugins/observability_solution/apm/server/routes/service_groups/get_service_group_alerts.ts b/x-pack/plugins/observability_solution/apm/server/routes/service_groups/get_service_group_alerts.ts index 8516527d82b9..d934863f37e9 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/service_groups/get_service_group_alerts.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/service_groups/get_service_group_alerts.ts @@ -5,10 +5,11 @@ * 2.0. */ -import { kqlQuery } from '@kbn/observability-plugin/server'; -import { ALERT_RULE_PRODUCER, ALERT_STATUS } from '@kbn/rule-data-utils'; +import { kqlQuery, termQuery, termsQuery } from '@kbn/observability-plugin/server'; +import { ALERT_RULE_PRODUCER, ALERT_STATUS, ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { Logger } from '@kbn/core/server'; +import { observabilityFeatureId } from '@kbn/observability-shared-plugin/common'; import { ApmPluginRequestHandlerContext } from '../typings'; import { SavedServiceGroup } from '../../../common/service_groups'; import { ApmAlertsClient } from '../../lib/helpers/get_apm_alerts_client'; @@ -42,8 +43,8 @@ export async function getServiceGroupAlerts({ query: { bool: { filter: [ - { term: { [ALERT_RULE_PRODUCER]: 'apm' } }, - { term: { [ALERT_STATUS]: 'active' } }, + ...termsQuery(ALERT_RULE_PRODUCER, 'apm', observabilityFeatureId), + ...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), ], }, }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_transaction_groups_alerts.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_transaction_groups_alerts.ts index 5bdb47cbb1f5..de9c73a30a18 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_transaction_groups_alerts.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_service_transaction_groups_alerts.ts @@ -5,13 +5,20 @@ * 2.0. */ -import { kqlQuery, termQuery, rangeQuery, wildcardQuery } from '@kbn/observability-plugin/server'; +import { + kqlQuery, + termQuery, + rangeQuery, + wildcardQuery, + termsQuery, +} from '@kbn/observability-plugin/server'; import { ALERT_RULE_PRODUCER, ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_RULE_PARAMETERS, } from '@kbn/rule-data-utils'; +import { observabilityFeatureId } from '@kbn/observability-shared-plugin/common'; import { SERVICE_NAME, TRANSACTION_NAME, TRANSACTION_TYPE } from '../../../common/es_fields/apm'; import { LatencyAggregationType } from '../../../common/latency_aggregation_types'; import { AggregationType } from '../../../common/rules/apm_rule_types'; @@ -59,7 +66,7 @@ export async function getServiceTransactionGroupsAlerts({ query: { bool: { filter: [ - ...termQuery(ALERT_RULE_PRODUCER, 'apm'), + ...termsQuery(ALERT_RULE_PRODUCER, 'apm', observabilityFeatureId), ...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), ...rangeQuery(start, end), ...kqlQuery(kuery), diff --git a/x-pack/plugins/observability_solution/apm/server/routes/services/get_services/get_service_alerts.ts b/x-pack/plugins/observability_solution/apm/server/routes/services/get_services/get_service_alerts.ts index b225f3ab70ef..01a125f45644 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/services/get_services/get_service_alerts.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/services/get_services/get_service_alerts.ts @@ -5,13 +5,20 @@ * 2.0. */ -import { kqlQuery, termQuery, rangeQuery, wildcardQuery } from '@kbn/observability-plugin/server'; +import { + kqlQuery, + termQuery, + rangeQuery, + wildcardQuery, + termsQuery, +} from '@kbn/observability-plugin/server'; import { ALERT_RULE_PRODUCER, ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_UUID, } from '@kbn/rule-data-utils'; +import { observabilityFeatureId } from '@kbn/observability-shared-plugin/common'; import { SERVICE_NAME } from '../../../../common/es_fields/apm'; import { ServiceGroup } from '../../../../common/service_groups'; import { ApmAlertsClient } from '../../../lib/helpers/get_apm_alerts_client'; @@ -51,7 +58,7 @@ export async function getServicesAlerts({ query: { bool: { filter: [ - ...termQuery(ALERT_RULE_PRODUCER, 'apm'), + ...termsQuery(ALERT_RULE_PRODUCER, 'apm', observabilityFeatureId), ...termQuery(ALERT_STATUS, ALERT_STATUS_ACTIVE), ...rangeQuery(start, end), ...kqlQuery(kuery), diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/agent_explorer.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/agent_explorer.spec.ts index 95d438ba87ca..28471d205592 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/agent_explorer.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/agent_explorer.spec.ts @@ -27,7 +27,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon APIClientRequestParamsOf<'GET /internal/apm/get_agents_per_service'>['params'] > ) { - return await apmApiClient.readUser({ + return apmApiClient.readUser({ endpoint: 'GET /internal/apm/get_agents_per_service', params: { query: { diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts index acc31a3743da..aa099ca66d23 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/agent_explorer/latest_agent_versions.spec.ts @@ -14,7 +14,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon const unlistedAgentName = 'unlistedAgent'; async function callApi() { - return await apmApiClient.readUser({ + return apmApiClient.readUser({ endpoint: 'GET /internal/apm/get_latest_agent_versions', }); } diff --git a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/error_count_threshold.spec.ts similarity index 67% rename from x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/error_count_threshold.spec.ts index 8b72afc194e3..16493e8220f6 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/error_count_threshold.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/error_count_threshold.spec.ts @@ -10,29 +10,30 @@ import { errorCountActionVariables } from '@kbn/apm-plugin/server/routes/alerts/ import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { omit } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { RoleCredentials, SupertestWithRoleScopeType } from '../../../../services'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { - createApmRule, fetchServiceInventoryAlertCounts, fetchServiceTabAlertCount, ApmAlertFields, - createIndexConnector, getIndexAction, -} from './helpers/alerting_api_helper'; -import { cleanupRuleAndAlertState } from './helpers/cleanup_rule_and_alert_state'; -import { waitForAlertsForRule } from './helpers/wait_for_alerts_for_rule'; -import { waitForIndexConnectorResults } from './helpers/wait_for_index_connector_results'; -import { waitForActiveRule } from './helpers/wait_for_active_rule'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const supertest = getService('supertest'); - const es = getService('es'); - const logger = getService('log'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - registry.when('error count threshold alert', { config: 'basic', archives: [] }, () => { + APM_ACTION_VARIABLE_INDEX, + APM_ALERTS_INDEX, +} from './helpers/alerting_helper'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const alertingApi = getService('alertingApi'); + const samlAuth = getService('samlAuth'); + + describe('error count threshold alert', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; + let roleAuthc: RoleCredentials; + const javaErrorMessage = 'a java error'; const phpErrorMessage = 'a php error'; @@ -50,7 +51,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { ], }; - before(() => { + before(async () => { + supertestViewerWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( + 'viewer', + { + withInternalHeaders: true, + useCookieHeader: true, + } + ); + + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + const opbeansJava = apm .service({ name: 'opbeans-java', environment: 'production', agentName: 'java' }) .instance('instance'); @@ -95,13 +106,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { ]; }); + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + return Promise.all([ apmSynthtraceEsClient.index(events), apmSynthtraceEsClient.index(phpEvents), ]); }); - after(() => apmSynthtraceEsClient.clean()); + after(async () => { + await apmSynthtraceEsClient.clean(); + await supertestViewerWithCookieCredentials.destroy(); + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); describe('create rule without kql filter', () => { let ruleId: string; @@ -109,31 +126,58 @@ export default function ApiTest({ getService }: FtrProviderContext) { let actionId: string; before(async () => { - actionId = await createIndexConnector({ supertest, name: 'Transation error count' }); + actionId = await alertingApi.createIndexConnector({ + name: 'Transation error count', + indexName: APM_ACTION_VARIABLE_INDEX, + roleAuthc, + }); + const indexAction = getIndexAction({ actionId, actionVariables: errorCountActionVariables, }); - const createdRule = await createApmRule({ - supertest, + + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.ErrorCount, name: 'Apm error count without kql query', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { ...ruleParams, }, actions: [indexAction], + roleAuthc, }); ruleId = createdRule.id; - alerts = await waitForAlertsForRule({ es, ruleId, minimumAlertCount: 2 }); + alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + docCountTarget: 2, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; }); - after(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + after(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('checks if rule is active', async () => { - const ruleStatus = await waitForActiveRule({ ruleId, supertest }); + const ruleStatus = await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); expect(ruleStatus).to.be('active'); }); @@ -141,7 +185,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { let results: Array>; before(async () => { - results = await waitForIndexConnectorResults({ es, minCount: 2 }); + await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); + + results = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ACTION_VARIABLE_INDEX, + docCountTarget: 2, + }) + ).hits.hits.map((hit) => hit._source) as Array>; }); it('produces a index action document for each service', async () => { @@ -151,6 +206,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { ]); }); + it('checks if rule is active', async () => { + const ruleStatus = await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); + expect(ruleStatus).to.be('active'); + }); + it('has the right keys', async () => { const phpEntry = results.find((result) => result.serviceName === 'opbeans-php')!; expect(Object.keys(phpEntry).sort()).to.eql([ @@ -170,7 +234,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('has the right values', () => { const phpEntry = results.find((result) => result.serviceName === 'opbeans-php')!; - expect(omit(phpEntry, 'alertDetailsUrl')).to.eql({ + expect(omit(phpEntry, 'alertDetailsUrl', 'viewInAppUrl')).to.eql({ environment: 'production', interval: '1 hr', reason: @@ -181,9 +245,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { errorGroupingName: 'a php error', threshold: '1', triggerValue: '30', - viewInAppUrl: - 'http://mockedPublicBaseUrl/app/apm/services/opbeans-php/errors?environment=production', }); + + const url = new URL(phpEntry.viewInAppUrl); + + expect(url.pathname).to.equal('/app/apm/services/opbeans-php/errors'); + expect(url.searchParams.get('environment')).to.equal('production'); }); }); @@ -255,30 +322,48 @@ export default function ApiTest({ getService }: FtrProviderContext) { let ruleId: string; before(async () => { - const createdRule = await createApmRule({ - supertest, + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.ErrorCount, name: 'Apm error count with kql query', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { + ...ruleParams, searchConfiguration: { query: { query: 'service.name: opbeans-php', language: 'kuery', }, }, - ...ruleParams, }, actions: [], + roleAuthc, }); + ruleId = createdRule.id; }); - after(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + after(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('produces one alert for the opbeans-php service', async () => { - const alerts = await waitForAlertsForRule({ es, ruleId }); + const alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; + expect(alerts[0]['kibana.alert.reason']).to.be( 'Error count is 30 in the last 1 hr for service: opbeans-php, env: production, name: tx-php, error key: 000000000000000000000a php error, error name: a php error. Alert when > 1.' ); diff --git a/x-pack/test/apm_api_integration/tests/alerts/generate_data.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/generate_data.ts similarity index 100% rename from x-pack/test/apm_api_integration/tests/alerts/generate_data.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/generate_data.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts new file mode 100644 index 000000000000..e5dcd4a45ac3 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; +import { ObservabilityApmAlert } from '@kbn/alerts-as-data-utils'; +import type { ApmApiClient } from '../../../../../services/apm_api'; + +export const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-*'; +export const APM_ACTION_VARIABLE_INDEX = 'apm-index-connector-test'; + +function getTimerange() { + return { + start: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), + end: new Date(Date.now() + 5 * 60 * 1000).toISOString(), + }; +} + +export async function fetchServiceInventoryAlertCounts(apmApiClient: ApmApiClient) { + const timerange = getTimerange(); + const serviceInventoryResponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services', + params: { + query: { + ...timerange, + environment: 'ENVIRONMENT_ALL', + kuery: '', + probability: 1, + documentType: ApmDocumentType.ServiceTransactionMetric, + rollupInterval: RollupInterval.SixtyMinutes, + useDurationSummary: true, + }, + }, + }); + + return serviceInventoryResponse.body.items.reduce>((acc, item) => { + return { ...acc, [item.serviceName]: item.alertsCount ?? 0 }; + }, {}); +} + +export async function fetchServiceTabAlertCount({ + apmApiClient, + serviceName, +}: { + apmApiClient: ApmApiClient; + serviceName: string; +}) { + const timerange = getTimerange(); + const alertsCountReponse = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/alerts_count', + params: { + path: { + serviceName, + }, + query: { + ...timerange, + environment: 'ENVIRONMENT_ALL', + }, + }, + }); + + return alertsCountReponse.body.alertsCount; +} + +export function getIndexAction({ + actionId, + actionVariables, +}: { + actionId: string; + actionVariables: Array<{ name: string }>; +}) { + return { + group: 'threshold_met', + id: actionId, + params: { + documents: [ + actionVariables.reduce>((acc, actionVariable) => { + acc[actionVariable.name] = `{{context.${actionVariable.name}}}`; + return acc; + }, {}), + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }; +} + +export type ApmAlertFields = ParsedTechnicalFields & ObservabilityApmAlert; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/index.ts new file mode 100644 index 000000000000..71661e4cbc8b --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('alerts', () => { + loadTestFile(require.resolve('./error_count_threshold.spec.ts')); + loadTestFile(require.resolve('./preview_chart_error_count.spec.ts')); + loadTestFile(require.resolve('./preview_chart_error_rate.spec.ts')); + loadTestFile(require.resolve('./preview_chart_transaction_duration.spec.ts')); + loadTestFile(require.resolve('./transaction_duration.spec.ts')); + loadTestFile(require.resolve('./transaction_error_rate.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_count.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_count.spec.ts similarity index 50% rename from x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_count.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_count.spec.ts index f09dbf1ad918..d6792400fc2b 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_count.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_count.spec.ts @@ -5,21 +5,21 @@ * 2.0. */ +import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; +import expect from '@kbn/expect'; +import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { + ERROR_GROUP_ID, SERVICE_ENVIRONMENT, SERVICE_NAME, - ERROR_GROUP_ID, -} from '@kbn/apm-plugin/common/es_fields/apm'; -import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; -import { getErrorGroupingKey } from '@kbn/apm-synthtrace-client/src/lib/apm/instance'; -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +} from '@kbn/observability-shared-plugin/common'; +import { generateLongIdWithSeed } from '@kbn/apm-synthtrace-client/src/lib/utils/generate_id'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { generateErrorData } from './generate_data'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; @@ -54,31 +54,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, }); - registry.when(`without data loaded`, { config: 'basic', archives: [] }, () => { - it('error_count (without data)', async () => { - const options = getOptions(); - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect(response.body.errorCountChartPreview.series).to.eql([]); - }); - }); - - registry.when.skip(`with data loaded`, { config: 'basic', archives: [] }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/172769 - describe('error_count: with data loaded', () => { - beforeEach(async () => { - await generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); - await generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); - }); - - afterEach(() => apmSynthtraceEsClient.clean()); - - it('with data', async () => { + describe('preview chart error count', () => { + describe(`without data loaded`, () => { + it('error_count (without data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({ @@ -87,230 +65,254 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); expect(response.status).to.be(200); - expect( - response.body.errorCountChartPreview.series.some((item: PreviewChartResponseItem) => - item.data.some((coordinate) => coordinate.x && coordinate.y) - ) - ).to.equal(true); + expect(response.body.errorCountChartPreview.series).to.eql([]); }); + }); - it('with error grouping key', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: 'synth-go', - errorGroupingKey: `${getErrorGroupingKey('Error 1')}`, - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }; - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', - ...options, + describe(`with data loaded`, () => { + describe('error_count: with data loaded', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await Promise.all([ + generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }), + generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }), + ]); }); - expect(response.status).to.be(200); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production', y: 250 }]); - }); + after(() => apmSynthtraceEsClient.clean()); - it('with no group by parameter', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', - }); + it('with data', async () => { + const options = getOptions(); - expect(response.status).to.be(200); - expect(response.body.errorCountChartPreview.series.length).to.equal(1); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production', y: 375 }]); - }); + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorCountChartPreview.series.some((item: PreviewChartResponseItem) => + item.data.some((coordinate) => coordinate.x && coordinate.y) + ) + ).to.equal(true); + }); - it('with default group by fields', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT], + it('with error grouping key', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: 'synth-go', + errorGroupingKey: `${generateLongIdWithSeed('Error 1')}`, + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, }, - }, - }; + }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production', y: 250 }]); }); - expect(response.status).to.be(200); - expect(response.body.errorCountChartPreview.series.length).to.equal(1); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production', y: 375 }]); - }); + it('with no group by parameter', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); - it('with group by on error grouping key', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview.series.length).to.equal(1); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production', y: 375 }]); + }); + + it('with default group by fields', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT], + }, }, - }, - }; + }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', - }); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); - expect(response.status).to.be(200); - expect(response.body.errorCountChartPreview.series.length).to.equal(2); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: `synth-go_production_${getErrorGroupingKey('Error 1')}`, - y: 250, - }, - { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, - y: 125, - }, - ]); - }); + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview.series.length).to.equal(1); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production', y: 375 }]); + }); - it('with group by on error grouping key and filter on error grouping key', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - errorGroupingKey: `${getErrorGroupingKey('Error 0')}`, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + it('with group by on error grouping key', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + }, }, - }, - }; + }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview.series.length).to.equal(2); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: `synth-go_production_${generateLongIdWithSeed('Error 1')}`, + y: 250, + }, + { + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, + y: 125, + }, + ]); }); - expect(response.status).to.be(200); - expect(response.body.errorCountChartPreview.series.length).to.equal(1); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, - y: 125, - }, - ]); - }); + it('with group by on error grouping key and filter on error grouping key', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + errorGroupingKey: `${generateLongIdWithSeed('Error 0')}`, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); - it('with empty service name', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: '', - environment: 'ENVIRONMENT_ALL', - interval: '5m', + expect(response.status).to.be(200); + expect(response.body.errorCountChartPreview.series.length).to.equal(1); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, + y: 125, }, - }, - }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + ]); }); - expect(response.status).to.be(200); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production', y: 375 }, - { name: 'synth-java_production', y: 375 }, - ]); - }); - - it('with empty service name and group by on error grouping key', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: '', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + it('with empty service name', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: '', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, }, - }, - }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production', y: 375 }, + { name: 'synth-java_production', y: 375 }, + ]); }); - expect(response.status).to.be(200); - expect( - response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: `synth-go_production_${getErrorGroupingKey('Error 1')}`, - y: 250, - }, - { - name: `synth-java_production_${getErrorGroupingKey('Error 1')}`, - y: 250, - }, - { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, - y: 125, - }, - { - name: `synth-java_production_${getErrorGroupingKey('Error 0')}`, - y: 125, - }, - ]); + it('with empty service name and group by on error grouping key', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: '', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, ERROR_GROUP_ID], + }, + }, + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/error_count/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorCountChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: `synth-go_production_${generateLongIdWithSeed('Error 1')}`, + y: 250, + }, + { + name: `synth-java_production_${generateLongIdWithSeed('Error 1')}`, + y: 250, + }, + { + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, + y: 125, + }, + { + name: `synth-java_production_${generateLongIdWithSeed('Error 0')}`, + y: 125, + }, + ]); + }); }); }); - }); - registry.when.skip( - `with data loaded and using KQL filter`, - { config: 'basic', archives: [] }, - () => { - // FLAKY: https://github.com/elastic/kibana/issues/176975 + describe(`with data loaded and using KQL filter`, () => { describe('error_count: with data loaded and using KQL filter', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); await generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); await generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); }); @@ -340,7 +342,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...getOptionsWithFilterQuery().params.query, searchConfiguration: JSON.stringify({ query: { - query: `service.name: synth-go and error.grouping_key: ${getErrorGroupingKey( + query: `service.name: synth-go and error.grouping_key: ${generateLongIdWithSeed( 'Error 1' )}`, language: 'kuery', @@ -430,11 +432,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { })) ).to.eql([ { - name: `synth-go_production_${getErrorGroupingKey('Error 1')}`, + name: `synth-go_production_${generateLongIdWithSeed('Error 1')}`, y: 250, }, { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, y: 125, }, ]); @@ -447,7 +449,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...getOptionsWithFilterQuery().params.query, searchConfiguration: JSON.stringify({ query: { - query: `service.name: synth-go and error.grouping_key: ${getErrorGroupingKey( + query: `service.name: synth-go and error.grouping_key: ${generateLongIdWithSeed( 'Error 0' )}`, language: 'kuery', @@ -472,7 +474,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { })) ).to.eql([ { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, y: 125, }, ]); @@ -539,24 +541,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { })) ).to.eql([ { - name: `synth-go_production_${getErrorGroupingKey('Error 1')}`, + name: `synth-go_production_${generateLongIdWithSeed('Error 1')}`, y: 250, }, { - name: `synth-java_production_${getErrorGroupingKey('Error 1')}`, + name: `synth-java_production_${generateLongIdWithSeed('Error 1')}`, y: 250, }, { - name: `synth-go_production_${getErrorGroupingKey('Error 0')}`, + name: `synth-go_production_${generateLongIdWithSeed('Error 0')}`, y: 125, }, { - name: `synth-java_production_${getErrorGroupingKey('Error 0')}`, + name: `synth-java_production_${generateLongIdWithSeed('Error 0')}`, y: 125, }, ]); }); }); - } - ); + }); + }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_rate.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_rate.spec.ts new file mode 100644 index 000000000000..3e5c0753fbc1 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_error_rate.spec.ts @@ -0,0 +1,607 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + TRANSACTION_TYPE, +} from '@kbn/observability-shared-plugin/common'; +import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; +import expect from '@kbn/expect'; +import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { generateErrorData } from './generate_data'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + const getOptions = () => ({ + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: 'synth-go', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }); + + const getOptionsWithFilterQuery = () => ({ + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + interval: '5m', + searchConfiguration: JSON.stringify({ + query: { + query: 'service.name: synth-go and transaction.type: request', + language: 'kuery', + }, + }), + serviceName: undefined, + transactionType: undefined, + transactionName: undefined, + environment: 'ENVIRONMENT_ALL', + }, + }, + }); + + describe('preview chart error rate', () => { + describe(`without data loaded`, () => { + it('transaction_error_rate without data', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series).to.eql([]); + }); + }); + + describe(`with data loaded`, () => { + describe('transaction_error_rate: with data loaded', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await Promise.all([ + generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }), + generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }), + ]); + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('with data', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.some((item: PreviewChartResponseItem) => + item.data.some((coordinate) => coordinate.x && coordinate.y) + ) + ).to.equal(true); + }); + + it('with transaction name', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: 'synth-go', + transactionName: 'GET /banana', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 50 }]); + }); + + it('with nonexistent transaction name', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: 'synth-go', + transactionName: 'foo', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series).to.eql([]); + }); + + it('with no group by parameter', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); + }); + + it('with default group by fields', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); + }); + + it('with group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(2); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: 'synth-go_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-go_production_request_GET /apple', + y: 25, + }, + ]); + }); + + it('with group by on transaction name and filter on transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + transactionName: 'GET /apple', + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 25 }]); + }); + + it('with empty service name, transaction name and transaction type', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: '', + transactionName: '', + transactionType: '', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request', y: 37.5 }, + { name: 'synth-java_production_request', y: 37.5 }, + ]); + }); + + it('with empty service name, transaction name, transaction type and group by on transaction name', async () => { + const options = { + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: '', + transactionName: '', + transactionType: '', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: 'synth-go_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-java_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-go_production_request_GET /apple', + y: 25, + }, + { + name: 'synth-java_production_request_GET /apple', + y: 25, + }, + ]); + }); + }); + }); + + describe(`with data loaded and using KQL filter`, () => { + describe('transaction_error_rate: with data loaded and using KQL filter', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await Promise.all([ + generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }), + generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }), + ]); + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('with data', async () => { + const options = getOptionsWithFilterQuery(); + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.some((item: PreviewChartResponseItem) => + item.data.some((coordinate) => coordinate.x && coordinate.y) + ) + ).to.equal(true); + }); + + it('with transaction name in filter query', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: GET /banana', + language: 'kuery', + }, + }), + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 50 }]); + }); + + it('with nonexistent transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: foo', + language: 'kuery', + }, + }), + }, + }, + }; + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series).to.eql([]); + }); + + it('with no group by parameter', async () => { + const options = getOptionsWithFilterQuery(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); + }); + + it('with default group by fields', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); + }); + + it('with group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(2); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: 'synth-go_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-go_production_request_GET /apple', + y: 25, + }, + ]); + }); + + it('with group by on transaction name and filter on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: GET /apple', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.errorRateChartPreview.series.length).to.equal(1); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 25 }]); + }); + + it('with empty filter query', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: '', + language: 'kuery', + }, + }), + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request', y: 37.5 }, + { name: 'synth-java_production_request', y: 37.5 }, + ]); + }); + + it('with empty filter query and group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: '', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { + name: 'synth-go_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-java_production_request_GET /banana', + y: 50, + }, + { + name: 'synth-go_production_request_GET /apple', + y: 25, + }, + { + name: 'synth-java_production_request_GET /apple', + y: 25, + }, + ]); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_transaction_duration.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_transaction_duration.spec.ts new file mode 100644 index 000000000000..af7f83c393a6 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/preview_chart_transaction_duration.spec.ts @@ -0,0 +1,564 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_NAME, + TRANSACTION_TYPE, +} from '@kbn/observability-shared-plugin/common'; +import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; +import expect from '@kbn/expect'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { generateLatencyData } from './generate_data'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + const getOptions = () => ({ + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + serviceName: 'synth-go', + transactionType: 'request', + environment: 'ENVIRONMENT_ALL', + interval: '5m', + }, + }, + }); + + const getOptionsWithFilterQuery = () => ({ + params: { + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + interval: '5m', + searchConfiguration: JSON.stringify({ + query: { + query: 'service.name: synth-go and transaction.type: request', + language: 'kuery', + }, + }), + serviceName: undefined, + transactionType: undefined, + transactionName: undefined, + environment: 'ENVIRONMENT_ALL', + }, + }, + }); + + describe('preview chart transaction duration', () => { + describe(`without data loaded`, () => { + it('transaction_duration (without data)', async () => { + const options = getOptions(); + + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + ...options, + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series).to.eql([]); + }); + }); + + describe(`with data loaded`, () => { + describe('transaction_duration: with data loaded', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + + await Promise.all([ + generateLatencyData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }), + generateLatencyData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }), + ]); + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('with data', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.some((item: PreviewChartResponseItem) => + item.data.some((coordinate) => coordinate.x && coordinate.y) + ) + ).to.equal(true); + }); + + it('with transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + transactionName: 'GET /banana', + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 5000 }]); + }); + + it('with nonexistent transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + transactionName: 'foo', + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series).to.eql([]); + }); + + it('with no group by parameter', async () => { + const options = getOptions(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); + }); + + it('with default group by fields', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); + }); + + it('with group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(2); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request_GET /apple', y: 10000 }, + { name: 'synth-go_production_request_GET /banana', y: 5000 }, + ]); + }); + + it('with group by on transaction name and filter on transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + transactionName: 'GET /apple', + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 10000 }]); + }); + + it('with empty service name, transaction name and transaction type', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + serviceName: '', + transactionName: '', + transactionType: '', + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request', y: 7500 }, + { name: 'synth-java_production_request', y: 7500 }, + ]); + }); + + it('with empty service name, transaction name, transaction type and group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptions().params.query, + serviceName: '', + transactionName: '', + transactionType: '', + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(4); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request_GET /apple', y: 10000 }, + { name: 'synth-java_production_request_GET /apple', y: 10000 }, + { name: 'synth-go_production_request_GET /banana', y: 5000 }, + { name: 'synth-java_production_request_GET /banana', y: 5000 }, + ]); + }); + }); + }); + + describe(`with data loaded and using KQL filter`, () => { + describe('transaction_duration: with data loaded and using KQL filter', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await Promise.all([ + generateLatencyData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }), + generateLatencyData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }), + ]); + }); + + after(() => apmSynthtraceEsClient.clean()); + + it('with data', async () => { + const options = getOptionsWithFilterQuery(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.some((item: PreviewChartResponseItem) => + item.data.some((coordinate) => coordinate.x && coordinate.y) + ) + ).to.equal(true); + }); + + it('with transaction name in filter query', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: GET /banana', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 5000 }]); + }); + + it('with nonexistent transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: foo', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series).to.eql([]); + }); + + it('with no group by parameter', async () => { + const options = getOptionsWithFilterQuery(); + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); + }); + + it('with default group by fields', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); + }); + + it('with group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(2); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request_GET /apple', y: 10000 }, + { name: 'synth-go_production_request_GET /banana', y: 5000 }, + ]); + }); + + it('with group by on transaction name and filter on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: + 'service.name: synth-go and transaction.type: request and transaction.name: GET /apple', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(1); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 10000 }]); + }); + + it('with empty filter query', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: '', + language: 'kuery', + }, + }), + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request', y: 7500 }, + { name: 'synth-java_production_request', y: 7500 }, + ]); + }); + + it('with empty filter query and group by on transaction name', async () => { + const options = { + params: { + query: { + ...getOptionsWithFilterQuery().params.query, + searchConfiguration: JSON.stringify({ + query: { + query: '', + language: 'kuery', + }, + }), + groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], + }, + }, + }; + + const response = await apmApiClient.readUser({ + ...options, + endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', + }); + + expect(response.status).to.be(200); + expect(response.body.latencyChartPreview.series.length).to.equal(4); + expect( + response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ + name: item.name, + y: item.data[0].y, + })) + ).to.eql([ + { name: 'synth-go_production_request_GET /apple', y: 10000 }, + { name: 'synth-java_production_request_GET /apple', y: 10000 }, + { name: 'synth-go_production_request_GET /banana', y: 5000 }, + { name: 'synth-java_production_request_GET /banana', y: 5000 }, + ]); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_duration.spec.ts similarity index 67% rename from x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_duration.spec.ts index 0d7460ff5be5..0cd344635955 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/transaction_duration.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_duration.spec.ts @@ -11,27 +11,24 @@ import { transactionDurationActionVariables } from '@kbn/apm-plugin/server/route import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { omit } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { RoleCredentials, SupertestWithRoleScopeType } from '../../../../services'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { - createApmRule, fetchServiceInventoryAlertCounts, fetchServiceTabAlertCount, ApmAlertFields, - createIndexConnector, getIndexAction, -} from './helpers/alerting_api_helper'; -import { cleanupRuleAndAlertState } from './helpers/cleanup_rule_and_alert_state'; -import { waitForAlertsForRule } from './helpers/wait_for_alerts_for_rule'; -import { waitForActiveRule } from './helpers/wait_for_active_rule'; -import { waitForIndexConnectorResults } from './helpers/wait_for_index_connector_results'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const supertest = getService('supertest'); - const es = getService('es'); - const logger = getService('log'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); + APM_ACTION_VARIABLE_INDEX, + APM_ALERTS_INDEX, +} from './helpers/alerting_helper'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const alertingApi = getService('alertingApi'); + const samlAuth = getService('samlAuth'); const ruleParams = { threshold: 3000, @@ -44,8 +41,22 @@ export default function ApiTest({ getService }: FtrProviderContext) { groupBy: ['service.name', 'service.environment', 'transaction.type', 'transaction.name'], }; - registry.when('transaction duration alert', { config: 'basic', archives: [] }, () => { - before(() => { + describe('transaction duration alert', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; + let roleAuthc: RoleCredentials; + + before(async () => { + supertestViewerWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( + 'viewer', + { + withInternalHeaders: true, + useCookieHeader: true, + } + ); + + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + const opbeansJava = apm .service({ name: 'opbeans-java', environment: 'production', agentName: 'java' }) .instance('instance'); @@ -68,11 +79,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success(), ]; }); + + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); return apmSynthtraceEsClient.index(events); }); after(async () => { await apmSynthtraceEsClient.clean(); + await supertestViewerWithCookieCredentials.destroy(); + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); }); describe('create rule for opbeans-java without kql filter', () => { @@ -81,31 +96,55 @@ export default function ApiTest({ getService }: FtrProviderContext) { let alerts: ApmAlertFields[]; before(async () => { - actionId = await createIndexConnector({ supertest, name: 'Transation duration' }); + actionId = await alertingApi.createIndexConnector({ + name: 'Transation duration', + indexName: APM_ACTION_VARIABLE_INDEX, + roleAuthc, + }); const indexAction = getIndexAction({ actionId, actionVariables: transactionDurationActionVariables, }); - const createdRule = await createApmRule({ - supertest, + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.TransactionDuration, name: 'Apm transaction duration without kql filter', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { ...ruleParams, }, actions: [indexAction], + roleAuthc, }); ruleId = createdRule.id; - alerts = await waitForAlertsForRule({ es, ruleId }); + alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; }); - after(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + after(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('checks if rule is active', async () => { - const ruleStatus = await waitForActiveRule({ ruleId, supertest }); + const ruleStatus = await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); expect(ruleStatus).to.be('active'); }); @@ -113,7 +152,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { let results: Array>; before(async () => { - results = await waitForIndexConnectorResults({ es }); + results = results = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ACTION_VARIABLE_INDEX, + }) + ).hits.hits.map((hit) => hit._source) as Array>; }); it('populates the action connector index with every action variable', async () => { @@ -133,7 +176,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('populates the document with the correct values', async () => { - expect(omit(results[0], 'alertDetailsUrl')).to.eql({ + expect(omit(results[0], 'alertDetailsUrl', 'viewInAppUrl')).to.eql({ environment: 'production', interval: '5 mins', reason: @@ -143,9 +186,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { transactionName: 'tx-java', threshold: '3000', triggerValue: '5,000 ms', - viewInAppUrl: - 'http://mockedPublicBaseUrl/app/apm/services/opbeans-java?transactionType=request&environment=production', }); + + const url = new URL(results[0].viewInAppUrl); + + expect(url.pathname).to.equal('/app/apm/services/opbeans-java'); + expect(url.searchParams.get('transactionType')).to.equal('request'); + expect(url.searchParams.get('environment')).to.equal('production'); }); }); @@ -192,10 +239,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { let alerts: ApmAlertFields[]; beforeEach(async () => { - const createdRule = await createApmRule({ - supertest, + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.TransactionDuration, name: 'Apm transaction duration with kql filter', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { searchConfiguration: { query: { @@ -207,17 +258,33 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...ruleParams, }, actions: [], + roleAuthc, }); ruleId = createdRule.id; - alerts = await waitForAlertsForRule({ es, ruleId }); + alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; }); - afterEach(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + afterEach(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('checks if rule is active', async () => { - const ruleStatus = await waitForActiveRule({ ruleId, supertest }); + const ruleStatus = await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); expect(ruleStatus).to.be('active'); }); diff --git a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_error_rate.spec.ts similarity index 69% rename from x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_error_rate.spec.ts index 509d839ecef6..e538ff0e6a3b 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/transaction_error_rate.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/alerts/transaction_error_rate.spec.ts @@ -10,30 +10,41 @@ import { transactionErrorRateActionVariables } from '@kbn/apm-plugin/server/rout import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { omit } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { RoleCredentials, SupertestWithRoleScopeType } from '../../../../services'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { - createApmRule, fetchServiceInventoryAlertCounts, fetchServiceTabAlertCount, ApmAlertFields, getIndexAction, - createIndexConnector, -} from './helpers/alerting_api_helper'; -import { cleanupRuleAndAlertState } from './helpers/cleanup_rule_and_alert_state'; -import { waitForAlertsForRule } from './helpers/wait_for_alerts_for_rule'; -import { waitForActiveRule } from './helpers/wait_for_active_rule'; -import { waitForIndexConnectorResults } from './helpers/wait_for_index_connector_results'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const supertest = getService('supertest'); - const es = getService('es'); - const logger = getService('log'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - - registry.when('transaction error rate alert', { config: 'basic', archives: [] }, () => { - before(() => { + APM_ACTION_VARIABLE_INDEX, + APM_ALERTS_INDEX, +} from './helpers/alerting_helper'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const alertingApi = getService('alertingApi'); + const samlAuth = getService('samlAuth'); + + describe('transaction error rate alert', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; + let roleAuthc: RoleCredentials; + + before(async () => { + supertestViewerWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( + 'viewer', + { + withInternalHeaders: true, + useCookieHeader: true, + } + ); + + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + const opbeansJava = apm .service({ name: 'opbeans-java', environment: 'production', agentName: 'java' }) .instance('instance'); @@ -66,11 +77,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { .success(), ]; }); + + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); return apmSynthtraceEsClient.index(events); }); after(async () => { await apmSynthtraceEsClient.clean(); + await supertestViewerWithCookieCredentials.destroy(); + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); }); describe('create rule without kql query', () => { @@ -79,16 +94,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { let alerts: ApmAlertFields[]; before(async () => { - actionId = await createIndexConnector({ supertest, name: 'Transation error rate' }); + actionId = await alertingApi.createIndexConnector({ + name: 'Transation error rate', + indexName: APM_ACTION_VARIABLE_INDEX, + roleAuthc, + }); + const indexAction = getIndexAction({ actionId, actionVariables: transactionErrorRateActionVariables, }); - const createdRule = await createApmRule({ - supertest, + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.TransactionErrorRate, name: 'Apm transaction error rate without kql query', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { threshold: 40, windowSize: 5, @@ -104,17 +128,33 @@ export default function ApiTest({ getService }: FtrProviderContext) { ], }, actions: [indexAction], + roleAuthc, }); ruleId = createdRule.id; - alerts = await waitForAlertsForRule({ es, ruleId }); + alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; }); - after(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + after(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('checks if rule is active', async () => { - const ruleStatus = await waitForActiveRule({ ruleId, supertest }); + const ruleStatus = await alertingApi.waitForRuleStatus({ + ruleId, + roleAuthc, + expectedStatus: 'active', + }); expect(ruleStatus).to.be('active'); }); @@ -122,7 +162,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { let results: Array>; before(async () => { - results = await waitForIndexConnectorResults({ es }); + results = results = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ACTION_VARIABLE_INDEX, + }) + ).hits.hits.map((hit) => hit._source) as Array>; }); it('has the right keys', async () => { @@ -142,7 +186,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('has the right values', () => { - expect(omit(results[0], 'alertDetailsUrl')).to.eql({ + expect(omit(results[0], 'alertDetailsUrl', 'viewInAppUrl')).to.eql({ environment: 'production', interval: '5 mins', reason: @@ -152,9 +196,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { threshold: '40', transactionType: 'request', triggerValue: '50', - viewInAppUrl: - 'http://mockedPublicBaseUrl/app/apm/services/opbeans-java?transactionType=request&environment=production', }); + + const url = new URL(results[0].viewInAppUrl); + + expect(url.pathname).to.equal('/app/apm/services/opbeans-java'); + expect(url.searchParams.get('transactionType')).to.equal('request'); + expect(url.searchParams.get('environment')).to.equal('production'); }); }); @@ -201,10 +249,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { let alerts: ApmAlertFields[]; beforeEach(async () => { - const createdRule = await createApmRule({ - supertest, + const createdRule = await alertingApi.createRule({ ruleTypeId: ApmRuleType.TransactionErrorRate, name: 'Apm transaction error rate without kql query', + consumer: 'apm', + schedule: { + interval: '1m', + }, + tags: ['apm'], params: { threshold: 40, windowSize: 5, @@ -227,14 +279,26 @@ export default function ApiTest({ getService }: FtrProviderContext) { ], }, actions: [], + roleAuthc, }); ruleId = createdRule.id; - alerts = await waitForAlertsForRule({ es, ruleId }); + alerts = ( + await alertingApi.waitForDocumentInIndex({ + indexName: APM_ALERTS_INDEX, + ruleId, + }) + ).hits.hits.map((hit) => hit._source) as ApmAlertFields[]; }); - afterEach(async () => { - await cleanupRuleAndAlertState({ es, supertest, logger }); - }); + afterEach(() => + alertingApi.cleanUpAlerts({ + roleAuthc, + ruleId, + alertIndexName: APM_ALERTS_INDEX, + connectorIndexName: APM_ACTION_VARIABLE_INDEX, + consumer: 'apm', + }) + ); it('indexes alert document with all group-by fields', async () => { expect(alerts[0]).property('service.name', 'opbeans-node'); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index 22bf6d8aa7f5..cc56d0f2e668 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -12,6 +12,7 @@ export default function apmApiIntegrationTests({ }: DeploymentAgnosticFtrProviderContext) { describe('APM', function () { loadTestFile(require.resolve('./agent_explorer')); + loadTestFile(require.resolve('./alerts')); loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); diff --git a/x-pack/test/api_integration/deployment_agnostic/services/alerting_api.ts b/x-pack/test/api_integration/deployment_agnostic/services/alerting_api.ts index 2956ee412a47..dd09804b5da8 100644 --- a/x-pack/test/api_integration/deployment_agnostic/services/alerting_api.ts +++ b/x-pack/test/api_integration/deployment_agnostic/services/alerting_api.ts @@ -11,8 +11,9 @@ import type { } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { MetricThresholdParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { ApmRuleParamsType } from '@kbn/apm-plugin/common/rules/schema'; import { RoleCredentials } from '@kbn/ftr-common-functional-services'; -import type { Client } from '@elastic/elasticsearch'; +import { errors, type Client } from '@elastic/elasticsearch'; import type { TryWithRetriesOptions } from '@kbn/ftr-common-functional-services'; import { v4 as uuidv4 } from 'uuid'; import moment from 'moment'; @@ -613,17 +614,6 @@ export function AlertingApiProvider({ getService }: DeploymentAgnosticFtrProvide return body; }, - async findRuleById(roleAuthc: RoleCredentials, ruleId: string) { - if (!ruleId) { - throw new Error(`'ruleId' is undefined`); - } - const response = await supertestWithoutAuth - .get(`/api/alerting/rule/${ruleId}`) - .set(samlAuth.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader); - return response.body || {}; - }, - waiting: { async waitForDocumentInIndex({ esClient, @@ -948,7 +938,6 @@ export function AlertingApiProvider({ getService }: DeploymentAgnosticFtrProvide return { helpers, - async waitForRuleStatus({ ruleId, expectedStatus, @@ -976,14 +965,27 @@ export function AlertingApiProvider({ getService }: DeploymentAgnosticFtrProvide async waitForDocumentInIndex({ indexName, docCountTarget = 1, + ruleId, }: { indexName: string; docCountTarget?: number; + ruleId?: string; }): Promise>> { return await retry.tryForTime(retryTimeout, async () => { const response = await es.search({ index: indexName, rest_total_hits_as_int: true, + ...(ruleId + ? { + body: { + query: { + term: { + 'kibana.alert.rule.uuid': ruleId, + }, + }, + }, + } + : {}), }); logger.debug(`Found ${response.hits.total} docs, looking for at least ${docCountTarget}.`); @@ -1064,7 +1066,15 @@ export function AlertingApiProvider({ getService }: DeploymentAgnosticFtrProvide }: { ruleTypeId: string; name: string; - params: MetricThresholdParams | ThresholdParams | SloBurnRateRuleParams; + params: + | CreateEsQueryRuleParams + | MetricThresholdParams + | ThresholdParams + | SloBurnRateRuleParams + | ApmRuleParamsType['apm.anomaly'] + | ApmRuleParamsType['apm.error_rate'] + | ApmRuleParamsType['apm.transaction_duration'] + | ApmRuleParamsType['apm.transaction_error_rate']; actions?: any[]; tags?: any[]; schedule?: { interval: string }; @@ -1096,5 +1106,103 @@ export function AlertingApiProvider({ getService }: DeploymentAgnosticFtrProvide .set(samlAuth.getInternalRequestHeader()); return response.body.data.find((obj: any) => obj.id === ruleId); }, + + async searchRules(roleAuthc: RoleCredentials, filter: string) { + return supertestWithoutAuth + .get('/api/alerting/rules/_find') + .query({ filter }) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()); + }, + + async deleteRuleById({ roleAuthc, ruleId }: { roleAuthc: RoleCredentials; ruleId: string }) { + return supertestWithoutAuth + .delete(`/api/alerting/rule/${ruleId}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()); + }, + + async deleteRules({ roleAuthc, filter }: { roleAuthc: RoleCredentials; filter: string }) { + const response = await this.searchRules(roleAuthc, filter); + return Promise.all( + response.body.data.map((rule: any) => this.deleteRuleById({ roleAuthc, ruleId: rule.id })) + ); + }, + + async deleteAllActionConnectors({ roleAuthc }: { roleAuthc: RoleCredentials }): Promise { + const res = await supertestWithoutAuth + .get(`/api/actions/connectors`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()); + + const body = res.body as Array<{ id: string; connector_type_id: string; name: string }>; + return Promise.all( + body.map(({ id }) => { + return this.deleteActionConnector({ + roleAuthc, + actionId: id, + }); + }) + ); + }, + + async deleteActionConnector({ + roleAuthc, + actionId, + }: { + roleAuthc: RoleCredentials; + actionId: string; + }) { + return supertestWithoutAuth + .delete(`/api/actions/connector/${actionId}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()); + }, + + async cleanUpAlerts({ + roleAuthc, + ruleId, + consumer, + alertIndexName, + connectorIndexName, + }: { + roleAuthc: RoleCredentials; + ruleId: string; + consumer?: string; + alertIndexName?: string; + connectorIndexName?: string; + }) { + return Promise.allSettled([ + // Delete the rule by ID + this.deleteRuleById({ roleAuthc, ruleId }), + // Delete all documents in the alert index if specified + alertIndexName + ? es.deleteByQuery({ + index: alertIndexName, + conflicts: 'proceed', + query: { match_all: {} }, + }) + : Promise.resolve(), + // Delete event logs for the specified consumer if provided + consumer + ? es.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': consumer } }, + }) + : Promise.resolve(), + // Delete connector index if provided + connectorIndexName + ? es.indices.delete({ index: connectorIndexName }).catch((e) => { + if (e instanceof errors.ResponseError && e.statusCode === 404) { + return; + } + + throw e; + }) + : Promise.resolve(), + // Delete all action connectors + this.deleteAllActionConnectors({ roleAuthc }), + ]); + }, }; } diff --git a/x-pack/test/api_integration/deployment_agnostic/services/apm_api.ts b/x-pack/test/api_integration/deployment_agnostic/services/apm_api.ts index 26d92997a602..c3f43b57902e 100644 --- a/x-pack/test/api_integration/deployment_agnostic/services/apm_api.ts +++ b/x-pack/test/api_integration/deployment_agnostic/services/apm_api.ts @@ -135,3 +135,5 @@ export function ApmApiProvider(context: DeploymentAgnosticFtrProviderContext) { writeUser: createApmApiClient(context, 'editor'), }; } + +export type ApmApiClient = ReturnType; diff --git a/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts index 033d64e8f12e..e88115389f58 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/anomaly_alert.spec.ts @@ -12,12 +12,12 @@ import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { range } from 'lodash'; import { ML_ANOMALY_SEVERITY } from '@kbn/ml-anomaly-utils/anomaly_severity'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { createAndRunApmMlJobs } from '../../common/utils/create_and_run_apm_ml_jobs'; -import { createApmRule } from './helpers/alerting_api_helper'; -import { waitForActiveRule } from './helpers/wait_for_active_rule'; import { waitForAlertsForRule } from './helpers/wait_for_alerts_for_rule'; +import { waitForActiveRule } from './helpers/wait_for_active_rule'; +import { createApmRule } from './helpers/alerting_api_helper'; import { cleanupRuleAndAlertState } from './helpers/cleanup_rule_and_alert_state'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { createAndRunApmMlJobs } from '../../common/utils/create_and_run_apm_ml_jobs'; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts index 5da6ee4f860d..86544981bbdb 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts @@ -11,13 +11,11 @@ import pRetry from 'p-retry'; import type { Agent as SuperTestAgent } from 'supertest'; import { ApmRuleType } from '@kbn/rule-data-utils'; import { ApmRuleParamsType } from '@kbn/apm-plugin/common/rules/schema'; -import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; -import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { ObservabilityApmAlert } from '@kbn/alerts-as-data-utils'; -import { ApmApiClient } from '../../../common/config'; - -export const APM_ALERTS_INDEX = '.alerts-observability.apm.alerts-*'; -export const APM_ACTION_VARIABLE_INDEX = 'apm-index-connector-test'; +import { + APM_ACTION_VARIABLE_INDEX, + APM_ALERTS_INDEX, +} from '../../../../api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper'; export async function createApmRule({ supertest, @@ -53,59 +51,6 @@ export async function createApmRule({ } } -function getTimerange() { - return { - start: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), - end: new Date(Date.now() + 5 * 60 * 1000).toISOString(), - }; -} - -export async function fetchServiceInventoryAlertCounts(apmApiClient: ApmApiClient) { - const timerange = getTimerange(); - const serviceInventoryResponse = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services', - params: { - query: { - ...timerange, - environment: 'ENVIRONMENT_ALL', - kuery: '', - probability: 1, - documentType: ApmDocumentType.ServiceTransactionMetric, - rollupInterval: RollupInterval.SixtyMinutes, - useDurationSummary: true, - }, - }, - }); - - return serviceInventoryResponse.body.items.reduce>((acc, item) => { - return { ...acc, [item.serviceName]: item.alertsCount ?? 0 }; - }, {}); -} - -export async function fetchServiceTabAlertCount({ - apmApiClient, - serviceName, -}: { - apmApiClient: ApmApiClient; - serviceName: string; -}) { - const timerange = getTimerange(); - const alertsCountReponse = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services/{serviceName}/alerts_count', - params: { - path: { - serviceName, - }, - query: { - ...timerange, - environment: 'ENVIRONMENT_ALL', - }, - }, - }); - - return alertsCountReponse.body.alertsCount; -} - export async function runRuleSoon({ ruleId, supertest, @@ -159,71 +104,6 @@ export async function deleteApmRules(supertest: SuperTestAgent) { ); } -export function deleteApmAlerts(es: Client) { - return es.deleteByQuery({ - index: APM_ALERTS_INDEX, - conflicts: 'proceed', - query: { match_all: {} }, - }); -} - -export async function clearKibanaApmEventLog(es: Client) { - return es.deleteByQuery({ - index: '.kibana-event-log-*', - query: { term: { 'kibana.alert.rule.consumer': 'apm' } }, - }); -} - -export type ApmAlertFields = ParsedTechnicalFields & ObservabilityApmAlert; - -export async function createIndexConnector({ - supertest, - name, -}: { - supertest: SuperTestAgent; - name: string; -}) { - const { body } = await supertest - .post(`/api/actions/connector`) - .set('kbn-xsrf', 'foo') - .send({ - name, - config: { - index: APM_ACTION_VARIABLE_INDEX, - refresh: true, - }, - connector_type_id: '.index', - }); - - return body.id as string; -} - -export function getIndexAction({ - actionId, - actionVariables, -}: { - actionId: string; - actionVariables: Array<{ name: string }>; -}) { - return { - group: 'threshold_met', - id: actionId, - params: { - documents: [ - actionVariables.reduce>((acc, actionVariable) => { - acc[actionVariable.name] = `{{context.${actionVariable.name}}}`; - return acc; - }, {}), - ], - }, - frequency: { - notify_when: 'onActionGroupChange', - throttle: null, - summary: false, - }, - }; -} - export async function deleteAllActionConnectors({ supertest, es, @@ -241,6 +121,23 @@ export async function deleteAllActionConnectors({ ); } +export function deleteApmAlerts(es: Client) { + return es.deleteByQuery({ + index: APM_ALERTS_INDEX, + conflicts: 'proceed', + query: { match_all: {} }, + }); +} + +export async function clearKibanaApmEventLog(es: Client) { + return es.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'kibana.alert.rule.consumer': 'apm' } }, + }); +} + +export type ApmAlertFields = ParsedTechnicalFields & ObservabilityApmAlert; + async function deleteActionConnector({ supertest, actionId, diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts index 2fae6c9643ff..b41e59cd4b77 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts @@ -9,12 +9,11 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; import type { Agent as SuperTestAgent } from 'supertest'; import { + deleteActionConnectorIndex, clearKibanaApmEventLog, - deleteApmRules, deleteApmAlerts, - deleteActionConnectorIndex, - deleteAllActionConnectors, } from './alerting_api_helper'; +import { deleteApmRules, deleteAllActionConnectors } from './alerting_api_helper'; export async function cleanupRuleAndAlertState({ es, diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_apm_alerts.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_apm_alerts.ts index 9f83b4850dd4..e342e31ee0fa 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_apm_alerts.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_apm_alerts.ts @@ -5,9 +5,9 @@ * 2.0. */ import type { Client } from '@elastic/elasticsearch'; -import pRetry from 'p-retry'; import { ToolingLog } from '@kbn/tooling-log'; -import { APM_ALERTS_INDEX } from './alerting_api_helper'; +import pRetry from 'p-retry'; +import { APM_ALERTS_INDEX } from '../../../../api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper'; export async function getActiveApmAlerts({ ruleId, diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_alerts_for_rule.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_alerts_for_rule.ts index 334631b354cd..28437e4edbc6 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_alerts_for_rule.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_alerts_for_rule.ts @@ -11,7 +11,10 @@ import type { SearchResponse, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import pRetry from 'p-retry'; -import { ApmAlertFields, APM_ALERTS_INDEX } from './alerting_api_helper'; +import { + APM_ALERTS_INDEX, + ApmAlertFields, +} from '../../../../api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper'; async function getAlertByRuleId({ es, ruleId }: { es: Client; ruleId: string }) { const response = (await es.search({ diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_index_connector_results.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_index_connector_results.ts index 964628942080..b6634e3a33f2 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_index_connector_results.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_index_connector_results.ts @@ -7,7 +7,7 @@ import { Client } from '@elastic/elasticsearch'; import pRetry from 'p-retry'; -import { APM_ACTION_VARIABLE_INDEX } from './alerting_api_helper'; +import { APM_ACTION_VARIABLE_INDEX } from '../../../../api_integration/deployment_agnostic/apis/observability/apm/alerts/helpers/alerting_helper'; async function getIndexConnectorResults(es: Client) { const res = await es.search({ index: APM_ACTION_VARIABLE_INDEX }); diff --git a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_rate.spec.ts deleted file mode 100644 index 3af62d826fd3..000000000000 --- a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_error_rate.spec.ts +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - SERVICE_ENVIRONMENT, - SERVICE_NAME, - TRANSACTION_NAME, - TRANSACTION_TYPE, -} from '@kbn/apm-plugin/common/es_fields/apm'; -import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { generateErrorData } from './generate_data'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - const getOptions = () => ({ - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: 'synth-go', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }); - - const getOptionsWithFilterQuery = () => ({ - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - interval: '5m', - searchConfiguration: JSON.stringify({ - query: { - query: 'service.name: synth-go and transaction.type: request', - language: 'kuery', - }, - }), - serviceName: undefined, - transactionType: undefined, - transactionName: undefined, - environment: 'ENVIRONMENT_ALL', - }, - }, - }); - - registry.when(`without data loaded`, { config: 'basic', archives: [] }, () => { - it('transaction_error_rate without data', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series).to.eql([]); - }); - }); - - registry.when(`with data loaded`, { config: 'basic', archives: [] }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/176977 - describe('transaction_error_rate: with data loaded', () => { - before(async () => { - await generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); - await generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('with data', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.some((item: PreviewChartResponseItem) => - item.data.some((coordinate) => coordinate.x && coordinate.y) - ) - ).to.equal(true); - }); - - it('with transaction name', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: 'synth-go', - transactionName: 'GET /banana', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }; - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 50 }]); - }); - - it('with nonexistent transaction name', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: 'synth-go', - transactionName: 'foo', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }; - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series).to.eql([]); - }); - - it('with no group by parameter', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); - }); - - it('with default group by fields', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); - }); - - it('with group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(2); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: 'synth-go_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-go_production_request_GET /apple', - y: 25, - }, - ]); - }); - - it('with group by on transaction name and filter on transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - transactionName: 'GET /apple', - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 25 }]); - }); - - it.skip('with empty service name, transaction name and transaction type', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: '', - transactionName: '', - transactionType: '', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request', y: 37.5 }, - { name: 'synth-java_production_request', y: 37.5 }, - ]); - }); - - it('with empty service name, transaction name, transaction type and group by on transaction name', async () => { - const options = { - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: '', - transactionName: '', - transactionType: '', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: 'synth-go_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-java_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-go_production_request_GET /apple', - y: 25, - }, - { - name: 'synth-java_production_request_GET /apple', - y: 25, - }, - ]); - }); - }); - }); - - registry.when(`with data loaded and using KQL filter`, { config: 'basic', archives: [] }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/176983 - describe('transaction_error_rate: with data loaded and using KQL filter', () => { - before(async () => { - await generateErrorData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); - await generateErrorData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('with data', async () => { - const options = getOptionsWithFilterQuery(); - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.some((item: PreviewChartResponseItem) => - item.data.some((coordinate) => coordinate.x && coordinate.y) - ) - ).to.equal(true); - }); - - it('with transaction name in filter query', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: GET /banana', - language: 'kuery', - }, - }), - }, - }, - }; - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 50 }]); - }); - - it('with nonexistent transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: foo', - language: 'kuery', - }, - }), - }, - }, - }; - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series).to.eql([]); - }); - - it('with no group by parameter', async () => { - const options = getOptionsWithFilterQuery(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); - }); - - it('with default group by fields', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 37.5 }]); - }); - - it('with group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(2); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: 'synth-go_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-go_production_request_GET /apple', - y: 25, - }, - ]); - }); - - it('with group by on transaction name and filter on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: GET /apple', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.errorRateChartPreview.series.length).to.equal(1); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 25 }]); - }); - - it('with empty filter query', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: '', - language: 'kuery', - }, - }), - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request', y: 37.5 }, - { name: 'synth-java_production_request', y: 37.5 }, - ]); - }); - - it.skip('with empty filter query and group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: '', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_error_rate/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.errorRateChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { - name: 'synth-go_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-java_production_request_GET /banana', - y: 50, - }, - { - name: 'synth-go_production_request_GET /apple', - y: 25, - }, - { - name: 'synth-java_production_request_GET /apple', - y: 25, - }, - ]); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_transaction_duration.spec.ts b/x-pack/test/apm_api_integration/tests/alerts/preview_chart_transaction_duration.spec.ts deleted file mode 100644 index a677ce11cdb0..000000000000 --- a/x-pack/test/apm_api_integration/tests/alerts/preview_chart_transaction_duration.spec.ts +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - SERVICE_ENVIRONMENT, - SERVICE_NAME, - TRANSACTION_NAME, - TRANSACTION_TYPE, -} from '@kbn/apm-plugin/common/es_fields/apm'; -import type { PreviewChartResponseItem } from '@kbn/apm-plugin/server/routes/alerts/route'; -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { generateLatencyData } from './generate_data'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); - const start = new Date('2021-01-01T00:00:00.000Z').getTime(); - const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - - const getOptions = () => ({ - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - serviceName: 'synth-go', - transactionType: 'request', - environment: 'ENVIRONMENT_ALL', - interval: '5m', - }, - }, - }); - - const getOptionsWithFilterQuery = () => ({ - params: { - query: { - start: new Date(start).toISOString(), - end: new Date(end).toISOString(), - interval: '5m', - searchConfiguration: JSON.stringify({ - query: { - query: 'service.name: synth-go and transaction.type: request', - language: 'kuery', - }, - }), - serviceName: undefined, - transactionType: undefined, - transactionName: undefined, - environment: 'ENVIRONMENT_ALL', - }, - }, - }); - - registry.when(`without data loaded`, { config: 'basic', archives: [] }, () => { - it('transaction_duration (without data)', async () => { - const options = getOptions(); - - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - ...options, - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series).to.eql([]); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/176989 - registry.when(`with data loaded`, { config: 'basic', archives: [] }, () => { - // FLAKY: https://github.com/elastic/kibana/issues/176986 - // Failing: See https://github.com/elastic/kibana/issues/176989 - describe('transaction_duration: with data loaded', () => { - before(async () => { - await generateLatencyData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); - await generateLatencyData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('with data', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.some((item: PreviewChartResponseItem) => - item.data.some((coordinate) => coordinate.x && coordinate.y) - ) - ).to.equal(true); - }); - - it('with transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - transactionName: 'GET /banana', - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 5000 }]); - }); - - it('with nonexistent transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - transactionName: 'foo', - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series).to.eql([]); - }); - - it('with no group by parameter', async () => { - const options = getOptions(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); - }); - - it('with default group by fields', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); - }); - - it('with group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(2); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request_GET /apple', y: 10000 }, - { name: 'synth-go_production_request_GET /banana', y: 5000 }, - ]); - }); - - it('with group by on transaction name and filter on transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - transactionName: 'GET /apple', - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 10000 }]); - }); - - it('with empty service name, transaction name and transaction type', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - serviceName: '', - transactionName: '', - transactionType: '', - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request', y: 7500 }, - { name: 'synth-java_production_request', y: 7500 }, - ]); - }); - - it('with empty service name, transaction name, transaction type and group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptions().params.query, - serviceName: '', - transactionName: '', - transactionType: '', - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(4); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request_GET /apple', y: 10000 }, - { name: 'synth-java_production_request_GET /apple', y: 10000 }, - { name: 'synth-go_production_request_GET /banana', y: 5000 }, - { name: 'synth-java_production_request_GET /banana', y: 5000 }, - ]); - }); - }); - }); - - // FLAKY: https://github.com/elastic/kibana/issues/176989 - registry.when(`with data loaded and using KQL filter`, { config: 'basic', archives: [] }, () => { - describe('transaction_duration: with data loaded and using KQL filter', () => { - before(async () => { - await generateLatencyData({ serviceName: 'synth-go', start, end, apmSynthtraceEsClient }); - await generateLatencyData({ serviceName: 'synth-java', start, end, apmSynthtraceEsClient }); - }); - - after(() => apmSynthtraceEsClient.clean()); - - it('with data', async () => { - const options = getOptionsWithFilterQuery(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.some((item: PreviewChartResponseItem) => - item.data.some((coordinate) => coordinate.x && coordinate.y) - ) - ).to.equal(true); - }); - - it('with transaction name in filter query', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: GET /banana', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 5000 }]); - }); - - it('with nonexistent transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: foo', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series).to.eql([]); - }); - - it('with no group by parameter', async () => { - const options = getOptionsWithFilterQuery(); - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); - }); - - it('with default group by fields', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request', y: 7500 }]); - }); - - it('with group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(2); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request_GET /apple', y: 10000 }, - { name: 'synth-go_production_request_GET /banana', y: 5000 }, - ]); - }); - - it('with group by on transaction name and filter on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: - 'service.name: synth-go and transaction.type: request and transaction.name: GET /apple', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(1); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([{ name: 'synth-go_production_request_GET /apple', y: 10000 }]); - }); - - it('with empty filter query', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: '', - language: 'kuery', - }, - }), - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request', y: 7500 }, - { name: 'synth-java_production_request', y: 7500 }, - ]); - }); - - it('with empty filter query and group by on transaction name', async () => { - const options = { - params: { - query: { - ...getOptionsWithFilterQuery().params.query, - searchConfiguration: JSON.stringify({ - query: { - query: '', - language: 'kuery', - }, - }), - groupBy: [SERVICE_NAME, SERVICE_ENVIRONMENT, TRANSACTION_TYPE, TRANSACTION_NAME], - }, - }, - }; - - const response = await apmApiClient.readUser({ - ...options, - endpoint: 'GET /internal/apm/rule_types/transaction_duration/chart_preview', - }); - - expect(response.status).to.be(200); - expect(response.body.latencyChartPreview.series.length).to.equal(4); - expect( - response.body.latencyChartPreview.series.map((item: PreviewChartResponseItem) => ({ - name: item.name, - y: item.data[0].y, - })) - ).to.eql([ - { name: 'synth-go_production_request_GET /apple', y: 10000 }, - { name: 'synth-java_production_request_GET /apple', y: 10000 }, - { name: 'synth-go_production_request_GET /banana', y: 5000 }, - { name: 'synth-java_production_request_GET /banana', y: 5000 }, - ]); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts b/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts index 044006e27334..d39724b0570b 100644 --- a/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts +++ b/x-pack/test/apm_api_integration/tests/service_groups/service_group_count/service_group_count.spec.ts @@ -7,10 +7,10 @@ import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; import { ApmRuleType } from '@kbn/rule-data-utils'; import expect from '@kbn/expect'; +import { waitForActiveApmAlert } from '../../alerts/helpers/wait_for_active_apm_alerts'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { createApmRule } from '../../alerts/helpers/alerting_api_helper'; import { cleanupRuleAndAlertState } from '../../alerts/helpers/cleanup_rule_and_alert_state'; -import { waitForActiveApmAlert } from '../../alerts/helpers/wait_for_active_apm_alerts'; import { createServiceGroupApi, deleteAllServiceGroups, diff --git a/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts b/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts index 958c2ed88f46..e3324546c84d 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_alerts.spec.ts @@ -8,10 +8,10 @@ import expect from '@kbn/expect'; import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; import { ApmRuleType } from '@kbn/rule-data-utils'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { waitForAlertsForRule } from '../alerts/helpers/wait_for_alerts_for_rule'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createApmRule, runRuleSoon, ApmAlertFields } from '../alerts/helpers/alerting_api_helper'; import { waitForActiveRule } from '../alerts/helpers/wait_for_active_rule'; -import { waitForAlertsForRule } from '../alerts/helpers/wait_for_alerts_for_rule'; import { cleanupRuleAndAlertState } from '../alerts/helpers/cleanup_rule_and_alert_state'; export default function ServiceAlerts({ getService }: FtrProviderContext) { diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_alerts.spec.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_alerts.spec.ts index 6c009682e142..f6b2c7c3a74a 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_alerts.spec.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_alerts.spec.ts @@ -13,10 +13,10 @@ import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { apm, timerange } from '@kbn/apm-synthtrace-client'; import { AggregationType } from '@kbn/apm-plugin/common/rules/apm_rule_types'; import { ApmRuleType } from '@kbn/rule-data-utils'; +import { waitForAlertsForRule } from '../alerts/helpers/wait_for_alerts_for_rule'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createApmRule, runRuleSoon, ApmAlertFields } from '../alerts/helpers/alerting_api_helper'; import { waitForActiveRule } from '../alerts/helpers/wait_for_active_rule'; -import { waitForAlertsForRule } from '../alerts/helpers/wait_for_alerts_for_rule'; import { cleanupRuleAndAlertState } from '../alerts/helpers/cleanup_rule_and_alert_state'; type TransactionsGroupsMainStatistics = From a10eb1fe4e55aa0cfbbb4b12a8d740a867463283 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 12 Nov 2024 14:19:22 +0300 Subject: [PATCH 048/100] [UA][Core] Surface integrations with internal APIs in upgrade assistant (#199026) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary > In https://github.com/elastic/kibana/issues/117241 we're surfacing usage of APIs marked as `deprecated: true` in the Upgrade Assistant to help users prepare for a major upgrade. While internal APIs aren't really deprecated in the same sense we are making a breaking change by blocking external integrations with these APIs. Since this could be equally disruptive to users depending on these APIs it would help our users to surface such usage in the UA too. The `api` deprecations now have two sub types: 1. routes deprecations `options.deprecated: { … }` 2. access deprecations `options.access: 'internal'` This PR adds the second `api` deprecation subtype. The reason i kept one `api` deprecation type and i didnt create a new type is that they have exactly the same registration process but are triggered by different attributes. The `api` deprecation is fully managed by the core team internal services and are configured by the user through the route interface so it makes sense to keep them as one type. I also can see us adding more subtypes to this and just piggybacking on the current flow instead of duplicating it everytime. **Checklist** - [x] Create deprecation subtype - [x] Example plugin - [x] Surface the deprecation in UA - [x] Api access deprecation copy (@florent-leborgne ) - [x] Update README and code annotations - [x] Unit tests - [x] Integration tests Closes https://github.com/elastic/kibana/issues/194675 ### Design decisions: If the API has both route deprecation (`options.deprecated: { … }` ) AND is an internal api `options.access: 'internal'` The current behavior i went for in my PR: I show this API once in the UA under the internal access deprecation. While showing the route deprecation details if defined. This seems to make the most sense since users should stop using this API altogether. ### Copy decisions: @florent-leborgne wrote the copy for this deprecation subtype. image image ## Testing Run kibana locally with the test example plugin that has deprecated routes ``` yarn start --plugin-path=examples/routing_example --plugin-path=examples/developer_examples ``` The following comprehensive deprecated routes examples are registered inside the folder: `examples/routing_example/server/routes/deprecated_routes` Run them in the dev console to trigger the deprecation condition so they show up in the UA: ``` GET kbn:/api/routing_example/d/internal_deprecated_route?elasticInternalOrigin=false GET kbn:/internal/routing_example/d/internal_only_route?elasticInternalOrigin=false GET kbn:/internal/routing_example/d/internal_versioned_route?apiVersion=1&elasticInternalOrigin=false ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- examples/routing_example/common/index.ts | 5 +- .../server/routes/deprecated_routes/index.ts | 2 + .../routes/deprecated_routes/internal.ts | 54 +++++++ .../routes/deprecated_routes/versioned.ts | 98 ++++++++---- .../server/routes/message_routes.ts | 3 + .../core-deprecations-common/index.ts | 1 + .../core-deprecations-common/src/types.ts | 2 +- .../src/deprecations/api_deprecations.ts | 96 ------------ .../access/access_deprecations.ts | 62 ++++++++ .../api_deprecations/access/i18n_texts.ts | 101 ++++++++++++ .../api_deprecations/access/index.ts | 10 ++ .../api_deprecation_id.test.ts | 51 ++++++ .../api_deprecations/api_deprecation_id.ts | 22 +++ .../deprecations/api_deprecations/index.ts | 13 ++ .../register_api_depercation_info.test.ts} | 66 ++------ .../register_api_depercation_info.ts | 70 +++++++++ .../route}/i18n_texts.ts | 67 ++++---- .../api_deprecations/route/index.ts | 10 ++ .../route/route_deprecations.ts | 65 ++++++++ .../deprecations/api_deprecations/types.ts | 25 +++ .../src/deprecations/index.ts | 7 +- .../src/deprecations_service.test.ts | 5 +- .../src/routes/get.ts | 3 + .../src/routes/post_validation_handler.ts | 11 +- .../src/routes/resolve_deprecated_api.ts | 3 + .../src/request.ts | 9 +- .../src/router.ts | 23 ++- .../versioned_router/core_versioned_route.ts | 12 +- .../src/http_server.ts | 21 ++- .../src/lifecycle_handlers.test.ts | 27 ++++ .../core-http-server-internal/src/types.ts | 10 +- packages/core/http/core-http-server/index.ts | 5 +- .../core-http-server/src/http_contract.ts | 8 +- .../http/core-http-server/src/router/index.ts | 10 +- .../http/core-http-server/src/router/route.ts | 9 ++ .../core-http-server/src/router/router.ts | 19 ++- .../src/core_usage_stats.ts | 3 + .../collectors/core/core_usage_collector.ts | 12 ++ src/plugins/telemetry/schema/oss_plugins.json | 24 ++- x-pack/plugins/upgrade_assistant/README.md | 34 ++-- .../upgrade_assistant/api_deprecations.ts | 145 +++++++++++++----- 41 files changed, 916 insertions(+), 307 deletions(-) create mode 100644 examples/routing_example/server/routes/deprecated_routes/internal.ts delete mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/access_deprecations.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/i18n_texts.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/index.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.test.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/index.ts rename packages/core/deprecations/core-deprecations-server-internal/src/deprecations/{api_deprecations.test.ts => api_deprecations/register_api_depercation_info.test.ts} (89%) create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.ts rename packages/core/deprecations/core-deprecations-server-internal/src/deprecations/{ => api_deprecations/route}/i18n_texts.ts (67%) create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/index.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/route_deprecations.ts create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/types.ts diff --git a/examples/routing_example/common/index.ts b/examples/routing_example/common/index.ts index 5bec77ebe0c0..205144ad0f87 100644 --- a/examples/routing_example/common/index.ts +++ b/examples/routing_example/common/index.ts @@ -20,5 +20,8 @@ export const DEPRECATED_ROUTES = { DEPRECATED_ROUTE: '/api/routing_example/d/deprecated_route', REMOVED_ROUTE: '/api/routing_example/d/removed_route', MIGRATED_ROUTE: '/api/routing_example/d/migrated_route', - VERSIONED_ROUTE: '/api/routing_example/d/versioned', + VERSIONED_ROUTE: '/api/routing_example/d/versioned_route', + INTERNAL_DEPRECATED_ROUTE: '/api/routing_example/d/internal_deprecated_route', + INTERNAL_ONLY_ROUTE: '/internal/routing_example/d/internal_only_route', + VERSIONED_INTERNAL_ROUTE: '/internal/routing_example/d/internal_versioned_route', }; diff --git a/examples/routing_example/server/routes/deprecated_routes/index.ts b/examples/routing_example/server/routes/deprecated_routes/index.ts index 75dc0261ed1b..3fa535b171d9 100644 --- a/examples/routing_example/server/routes/deprecated_routes/index.ts +++ b/examples/routing_example/server/routes/deprecated_routes/index.ts @@ -10,8 +10,10 @@ import { IRouter } from '@kbn/core/server'; import { registerDeprecatedRoute } from './unversioned'; import { registerVersionedDeprecatedRoute } from './versioned'; +import { registerInternalDeprecatedRoute } from './internal'; export function registerDeprecatedRoutes(router: IRouter) { registerDeprecatedRoute(router); registerVersionedDeprecatedRoute(router); + registerInternalDeprecatedRoute(router); } diff --git a/examples/routing_example/server/routes/deprecated_routes/internal.ts b/examples/routing_example/server/routes/deprecated_routes/internal.ts new file mode 100644 index 000000000000..95267cb66dd3 --- /dev/null +++ b/examples/routing_example/server/routes/deprecated_routes/internal.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { IRouter } from '@kbn/core/server'; +import { DEPRECATED_ROUTES } from '../../../common'; + +export const registerInternalDeprecatedRoute = (router: IRouter) => { + router.get( + { + path: DEPRECATED_ROUTES.INTERNAL_DEPRECATED_ROUTE, + validate: false, + options: { + // Explicitly set access is to internal + access: 'internal', + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'critical', + message: 'Additonal message for internal deprecated api', + reason: { type: 'deprecate' }, + }, + }, + }, + async (ctx, req, res) => { + return res.ok({ + body: { + result: + 'Called deprecated route with `access: internal`. Check UA to see the deprecation.', + }, + }); + } + ); + + router.get( + { + path: DEPRECATED_ROUTES.INTERNAL_ONLY_ROUTE, + validate: false, + // If no access is specified then it defaults to internal + }, + async (ctx, req, res) => { + return res.ok({ + body: { + result: + 'Called route with `access: internal` Although this API is not marked as deprecated it will show in UA. Check UA to see the deprecation.', + }, + }); + } + ); +}; diff --git a/examples/routing_example/server/routes/deprecated_routes/versioned.ts b/examples/routing_example/server/routes/deprecated_routes/versioned.ts index 060bc64403db..6261ef6f9cb9 100644 --- a/examples/routing_example/server/routes/deprecated_routes/versioned.ts +++ b/examples/routing_example/server/routes/deprecated_routes/versioned.ts @@ -11,42 +11,72 @@ import type { IRouter } from '@kbn/core/server'; import { DEPRECATED_ROUTES } from '../../../common'; export const registerVersionedDeprecatedRoute = (router: IRouter) => { - const versionedRoute = router.versioned.get({ - path: DEPRECATED_ROUTES.VERSIONED_ROUTE, - description: 'Routing example plugin deprecated versioned route.', - access: 'internal', - options: { - excludeFromOAS: true, - }, - enableQueryVersion: true, - }); - - versionedRoute.addVersion( - { + router.versioned + .get({ + path: DEPRECATED_ROUTES.VERSIONED_ROUTE, + description: 'Routing example plugin deprecated versioned route.', + access: 'public', options: { - deprecated: { - documentationUrl: 'https://elastic.co/', - severity: 'warning', - reason: { type: 'bump', newApiVersion: '2' }, + excludeFromOAS: true, + }, + enableQueryVersion: true, + }) + .addVersion( + { + options: { + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'warning', + reason: { type: 'deprecate' }, + }, }, + validate: false, + version: '2023-10-31', }, - validate: false, - version: '1', - }, - (ctx, req, res) => { - return res.ok({ - body: { result: 'Called deprecated version of the API. API version 1 -> 2' }, - }); - } - ); + (ctx, req, res) => { + return res.ok({ + body: { result: 'Called deprecated version of the API "2023-10-31"' }, + }); + } + ); - versionedRoute.addVersion( - { - version: '2', - validate: false, - }, - (ctx, req, res) => { - return res.ok({ body: { result: 'Called API version 2' } }); - } - ); + router.versioned + .get({ + path: DEPRECATED_ROUTES.VERSIONED_INTERNAL_ROUTE, + description: 'Routing example plugin deprecated versioned route.', + access: 'internal', + options: { + excludeFromOAS: true, + }, + enableQueryVersion: true, + }) + .addVersion( + { + options: { + deprecated: { + documentationUrl: 'https://elastic.co/', + severity: 'warning', + reason: { type: 'bump', newApiVersion: '2' }, + }, + }, + validate: false, + version: '1', + }, + (ctx, req, res) => { + return res.ok({ + body: { result: 'Called internal deprecated version of the API 1.' }, + }); + } + ) + .addVersion( + { + validate: false, + version: '2', + }, + (ctx, req, res) => { + return res.ok({ + body: { result: 'Called non-deprecated version of the API.' }, + }); + } + ); }; diff --git a/examples/routing_example/server/routes/message_routes.ts b/examples/routing_example/server/routes/message_routes.ts index c4f4aea1cf3e..ccf200e811ff 100644 --- a/examples/routing_example/server/routes/message_routes.ts +++ b/examples/routing_example/server/routes/message_routes.ts @@ -63,6 +63,9 @@ export function registerGetMessageByIdRoute(router: IRouter) { router.get( { path: `${INTERNAL_GET_MESSAGE_BY_ID_ROUTE}/{id}`, + options: { + access: 'internal', + }, validate: { params: schema.object({ id: schema.string(), diff --git a/packages/core/deprecations/core-deprecations-common/index.ts b/packages/core/deprecations/core-deprecations-common/index.ts index 005e84d7d57f..de8122a18c55 100644 --- a/packages/core/deprecations/core-deprecations-common/index.ts +++ b/packages/core/deprecations/core-deprecations-common/index.ts @@ -11,6 +11,7 @@ export type { BaseDeprecationDetails, ConfigDeprecationDetails, FeatureDeprecationDetails, + ApiDeprecationDetails, DeprecationsDetails, DomainDeprecationDetails, DeprecationsGetResponse, diff --git a/packages/core/deprecations/core-deprecations-common/src/types.ts b/packages/core/deprecations/core-deprecations-common/src/types.ts index 85da30b2c128..9a08be780845 100644 --- a/packages/core/deprecations/core-deprecations-common/src/types.ts +++ b/packages/core/deprecations/core-deprecations-common/src/types.ts @@ -121,7 +121,7 @@ export type DeprecationsDetails = /** * @public */ -export type DomainDeprecationDetails = DeprecationsDetails & { +export type DomainDeprecationDetails = ExtendedDetails & { domainId: string; }; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts deleted file mode 100644 index 45893987ddf9..000000000000 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; -import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; -import type { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; -import { DeprecationsDetails } from '@kbn/core-deprecations-common'; -import type { DeprecationsFactory } from '../deprecations_factory'; -import { - getApiDeprecationMessage, - getApiDeprecationsManualSteps, - getApiDeprecationTitle, -} from './i18n_texts'; - -interface ApiDeprecationsServiceDeps { - deprecationsFactory: DeprecationsFactory; - http: InternalHttpServiceSetup; - coreUsageData: InternalCoreUsageDataSetup; -} - -export const buildApiDeprecationId = ({ - routePath, - routeMethod, - routeVersion, -}: Pick): string => { - return [ - routeVersion || 'unversioned', - routeMethod.toLocaleLowerCase(), - routePath.replace(/\/$/, ''), - ].join('|'); -}; - -export const createGetApiDeprecations = - ({ http, coreUsageData }: Pick) => - async (): Promise => { - const deprecatedRoutes = http.getRegisteredDeprecatedApis(); - const usageClient = coreUsageData.getClient(); - const deprecatedApiUsageStats = await usageClient.getDeprecatedApiUsageStats(); - - return deprecatedApiUsageStats - .filter(({ apiTotalCalls, totalMarkedAsResolved }) => { - return apiTotalCalls > totalMarkedAsResolved; - }) - .filter(({ apiId }) => - deprecatedRoutes.some((routeDetails) => buildApiDeprecationId(routeDetails) === apiId) - ) - .map((apiUsageStats) => { - const { apiId, apiTotalCalls, totalMarkedAsResolved } = apiUsageStats; - const routeDeprecationDetails = deprecatedRoutes.find( - (routeDetails) => buildApiDeprecationId(routeDetails) === apiId - )!; - const { routeVersion, routePath, routeDeprecationOptions, routeMethod } = - routeDeprecationDetails; - - const deprecationLevel = routeDeprecationOptions.severity || 'warning'; - - return { - apiId, - title: getApiDeprecationTitle(routeDeprecationDetails), - level: deprecationLevel, - message: getApiDeprecationMessage(routeDeprecationDetails, apiUsageStats), - documentationUrl: routeDeprecationOptions.documentationUrl, - correctiveActions: { - manualSteps: getApiDeprecationsManualSteps(routeDeprecationDetails), - mark_as_resolved_api: { - routePath, - routeMethod, - routeVersion, - apiTotalCalls, - totalMarkedAsResolved, - timestamp: new Date(), - }, - }, - deprecationType: 'api', - domainId: 'core.routes-deprecations', - }; - }); - }; - -export const registerApiDeprecationsInfo = ({ - deprecationsFactory, - http, - coreUsageData, -}: ApiDeprecationsServiceDeps): void => { - const deprecationsRegistery = deprecationsFactory.getRegistry('core.api_deprecations'); - - deprecationsRegistery.registerDeprecations({ - getDeprecations: createGetApiDeprecations({ http, coreUsageData }), - }); -}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/access_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/access_deprecations.ts new file mode 100644 index 000000000000..2a0c2a8cae5f --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/access_deprecations.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { + ApiDeprecationDetails, + DomainDeprecationDetails, +} from '@kbn/core-deprecations-common'; + +import type { PostValidationMetadata } from '@kbn/core-http-server'; +import type { BuildApiDeprecationDetailsParams } from '../types'; +import { + getApiDeprecationMessage, + getApiDeprecationsManualSteps, + getApiDeprecationTitle, +} from './i18n_texts'; + +export const getIsAccessApiDeprecation = ({ + isInternalApiRequest, + isPublicAccess, +}: PostValidationMetadata): boolean => { + const isNotPublicAccess = !isPublicAccess; + const isNotInternalRequest = !isInternalApiRequest; + + return !!(isNotPublicAccess && isNotInternalRequest); +}; + +export const buildApiAccessDeprecationDetails = ({ + apiUsageStats, + deprecatedApiDetails, +}: BuildApiDeprecationDetailsParams): DomainDeprecationDetails => { + const { apiId, apiTotalCalls, totalMarkedAsResolved } = apiUsageStats; + const { routeVersion, routePath, routeDeprecationOptions, routeMethod } = deprecatedApiDetails; + + const deprecationLevel = routeDeprecationOptions?.severity || 'warning'; + + return { + apiId, + title: getApiDeprecationTitle(deprecatedApiDetails), + level: deprecationLevel, + message: getApiDeprecationMessage(deprecatedApiDetails, apiUsageStats), + documentationUrl: routeDeprecationOptions?.documentationUrl, + correctiveActions: { + manualSteps: getApiDeprecationsManualSteps(), + mark_as_resolved_api: { + routePath, + routeMethod, + routeVersion, + apiTotalCalls, + totalMarkedAsResolved, + timestamp: new Date(), + }, + }, + deprecationType: 'api', + domainId: 'core.http.access-deprecations', + }; +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/i18n_texts.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/i18n_texts.ts new file mode 100644 index 000000000000..bd1f92cf368f --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/i18n_texts.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RouterDeprecatedApiDetails } from '@kbn/core-http-server'; +import { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; + +export const getApiDeprecationTitle = ( + details: Pick +) => { + const { routePath, routeMethod } = details; + const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; + + return i18n.translate('core.deprecations.apiAccessDeprecation.infoTitle', { + defaultMessage: 'The "{routeWithMethod}" API is internal to Elastic', + values: { + routeWithMethod, + }, + }); +}; + +export const getApiDeprecationMessage = ( + details: Pick< + RouterDeprecatedApiDetails, + 'routePath' | 'routeMethod' | 'routeDeprecationOptions' + >, + apiUsageStats: CoreDeprecatedApiUsageStats +): string[] => { + const { routePath, routeMethod, routeDeprecationOptions } = details; + const { apiLastCalledAt, apiTotalCalls, markedAsResolvedLastCalledAt, totalMarkedAsResolved } = + apiUsageStats; + + const diff = apiTotalCalls - totalMarkedAsResolved; + const wasResolvedBefore = totalMarkedAsResolved > 0; + const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; + + const messages = [ + i18n.translate('core.deprecations.apiAccessDeprecation.apiCallsDetailsMessage', { + defaultMessage: + 'The API "{routeWithMethod}" has been called {apiTotalCalls} times. The last call was on {apiLastCalledAt}.', + values: { + routeWithMethod, + apiTotalCalls, + apiLastCalledAt: moment(apiLastCalledAt).format('LLLL Z'), + }, + }), + ]; + + if (wasResolvedBefore) { + messages.push( + i18n.translate('core.deprecations.apiAccessDeprecation.previouslyMarkedAsResolvedMessage', { + defaultMessage: + 'This issue has been marked as resolved on {markedAsResolvedLastCalledAt} but the API has been called {timeSinceLastResolved, plural, one {# time} other {# times}} since.', + values: { + timeSinceLastResolved: diff, + markedAsResolvedLastCalledAt: moment(markedAsResolvedLastCalledAt).format('LLLL Z'), + }, + }) + ); + } + + messages.push( + i18n.translate('core.deprecations.apiAccessDeprecation.internalApiExplanationMessage', { + defaultMessage: + 'Internal APIs are meant to be used by Elastic services only. You should not use them. External access to these APIs will be restricted.', + }) + ); + + if (routeDeprecationOptions?.message) { + // Surfaces additional deprecation messages passed into the route in UA + messages.push(routeDeprecationOptions.message); + } + + return messages; +}; + +export const getApiDeprecationsManualSteps = (): string[] => { + return [ + i18n.translate('core.deprecations.apiAccessDeprecation.manualSteps.identifyCallsOriginStep', { + defaultMessage: 'Identify the origin of these API calls.', + }), + i18n.translate('core.deprecations.apiAccessDeprecation.manualSteps.deleteRequestsStep', { + defaultMessage: + 'Delete any requests you have that use this API. Check the learn more link for possible alternatives.', + }), + i18n.translate( + 'core.deprecations.apiAccessDeprecation.manualSteps.accessDepractionMarkAsResolvedStep', + { + defaultMessage: + 'Once you have successfully stopped using this API, mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.', + } + ), + ]; +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/index.ts new file mode 100644 index 000000000000..b78ddba35d2c --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/access/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { buildApiAccessDeprecationDetails, getIsAccessApiDeprecation } from './access_deprecations'; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.test.ts new file mode 100644 index 000000000000..151b7bd8d4ce --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { buildApiDeprecationId } from './api_deprecation_id'; + +describe('#buildApiDeprecationId', () => { + it('returns apiDeprecationId string for versioned routes', () => { + const apiDeprecationId = buildApiDeprecationId({ + routeMethod: 'get', + routePath: '/api/test', + routeVersion: '10-10-2023', + }); + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); + + it('returns apiDeprecationId string for unversioned routes', () => { + const apiDeprecationId = buildApiDeprecationId({ + routeMethod: 'get', + routePath: '/api/test', + }); + expect(apiDeprecationId).toBe('unversioned|get|/api/test'); + }); + + it('gives the same ID the route method is capitalized or not', () => { + const apiDeprecationId = buildApiDeprecationId({ + // @ts-expect-error + routeMethod: 'GeT', + routePath: '/api/test', + routeVersion: '10-10-2023', + }); + + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); + + it('gives the same ID the route path has a trailing slash or not', () => { + const apiDeprecationId = buildApiDeprecationId({ + // @ts-expect-error + routeMethod: 'GeT', + routePath: '/api/test/', + routeVersion: '10-10-2023', + }); + + expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); + }); +}); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.ts new file mode 100644 index 000000000000..0e3b6107a25e --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/api_deprecation_id.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { RouterDeprecatedApiDetails } from '@kbn/core-http-server'; + +export const buildApiDeprecationId = ({ + routePath, + routeMethod, + routeVersion, +}: Pick): string => { + return [ + routeVersion || 'unversioned', + routeMethod.toLocaleLowerCase(), + routePath.replace(/\/$/, ''), + ].join('|'); +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/index.ts new file mode 100644 index 000000000000..8d823e8ad211 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { registerApiDeprecationsInfo } from './register_api_depercation_info'; +export { getIsAccessApiDeprecation } from './access'; +export { getIsRouteApiDeprecation } from './route'; +export { buildApiDeprecationId } from './api_deprecation_id'; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.test.ts similarity index 89% rename from packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts rename to packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.test.ts index 5f9d0bfbb4b8..9a7842d8915d 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations.test.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.test.ts @@ -8,13 +8,13 @@ */ import type { DeepPartial } from '@kbn/utility-types'; -import { mockDeprecationsRegistry, mockDeprecationsFactory } from '../mocks'; +import { mockDeprecationsRegistry, mockDeprecationsFactory } from '../../mocks'; import { registerApiDeprecationsInfo, - buildApiDeprecationId, createGetApiDeprecations, -} from './api_deprecations'; -import { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; +} from './register_api_depercation_info'; +import { buildApiDeprecationId } from './api_deprecation_id'; +import { RouterDeprecatedApiDetails } from '@kbn/core-http-server'; import { httpServiceMock } from '@kbn/core-http-server-mocks'; import { coreUsageDataServiceMock, @@ -58,10 +58,11 @@ describe('#registerApiDeprecationsInfo', () => { describe('#createGetApiDeprecations', () => { const createDeprecatedRouteDetails = ( - overrides?: DeepPartial - ): RouterDeprecatedRouteDetails => + overrides?: DeepPartial + ): RouterDeprecatedApiDetails => _.merge( { + routeAccess: 'public', routeDeprecationOptions: { documentationUrl: 'https://fake-url', severity: 'critical', @@ -72,7 +73,7 @@ describe('#registerApiDeprecationsInfo', () => { routeMethod: 'get', routePath: '/api/test/', routeVersion: '123', - } as RouterDeprecatedRouteDetails, + } as RouterDeprecatedApiDetails, overrides ); @@ -124,7 +125,7 @@ describe('#registerApiDeprecationsInfo', () => { }, "deprecationType": "api", "documentationUrl": "https://fake-url", - "domainId": "core.routes-deprecations", + "domainId": "core.http.routes-deprecations", "level": "critical", "message": Array [ "The API \\"GET /api/test_removed/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", @@ -171,7 +172,7 @@ describe('#registerApiDeprecationsInfo', () => { }, "deprecationType": "api", "documentationUrl": "https://fake-url", - "domainId": "core.routes-deprecations", + "domainId": "core.http.routes-deprecations", "level": "critical", "message": Array [ "The API \\"GET /api/test_migrated/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", @@ -216,7 +217,7 @@ describe('#registerApiDeprecationsInfo', () => { }, "deprecationType": "api", "documentationUrl": "https://fake-url", - "domainId": "core.routes-deprecations", + "domainId": "core.http.routes-deprecations", "level": "critical", "message": Array [ "The API \\"GET /api/test_bumped/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", @@ -260,7 +261,7 @@ describe('#registerApiDeprecationsInfo', () => { }, "deprecationType": "api", "documentationUrl": "https://fake-url", - "domainId": "core.routes-deprecations", + "domainId": "core.http.routes-deprecations", "level": "critical", "message": Array [ "The API \\"GET /api/test_deprecated/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", @@ -323,7 +324,7 @@ describe('#registerApiDeprecationsInfo', () => { }, "deprecationType": "api", "documentationUrl": "https://fake-url", - "domainId": "core.routes-deprecations", + "domainId": "core.http.routes-deprecations", "level": "critical", "message": Array [ "The API \\"GET /api/test_never_resolved/\\" has been called 13 times. The last call was on Sunday, September 1, 2024 6:06 AM -04:00.", @@ -355,44 +356,3 @@ describe('#registerApiDeprecationsInfo', () => { }); }); }); - -describe('#buildApiDeprecationId', () => { - it('returns apiDeprecationId string for versioned routes', () => { - const apiDeprecationId = buildApiDeprecationId({ - routeMethod: 'get', - routePath: '/api/test', - routeVersion: '10-10-2023', - }); - expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); - }); - - it('returns apiDeprecationId string for unversioned routes', () => { - const apiDeprecationId = buildApiDeprecationId({ - routeMethod: 'get', - routePath: '/api/test', - }); - expect(apiDeprecationId).toBe('unversioned|get|/api/test'); - }); - - it('gives the same ID the route method is capitalized or not', () => { - const apiDeprecationId = buildApiDeprecationId({ - // @ts-expect-error - routeMethod: 'GeT', - routePath: '/api/test', - routeVersion: '10-10-2023', - }); - - expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); - }); - - it('gives the same ID the route path has a trailing slash or not', () => { - const apiDeprecationId = buildApiDeprecationId({ - // @ts-expect-error - routeMethod: 'GeT', - routePath: '/api/test/', - routeVersion: '10-10-2023', - }); - - expect(apiDeprecationId).toBe('10-10-2023|get|/api/test'); - }); -}); diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.ts new file mode 100644 index 000000000000..f4c9847b09e5 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/register_api_depercation_info.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { DeprecationsDetails } from '@kbn/core-deprecations-common'; + +import { buildApiRouteDeprecationDetails } from './route/route_deprecations'; +import { buildApiAccessDeprecationDetails } from './access/access_deprecations'; +import { buildApiDeprecationId } from './api_deprecation_id'; +import type { ApiDeprecationsServiceDeps } from './types'; + +export const createGetApiDeprecations = + ({ http, coreUsageData }: Pick) => + async (): Promise => { + const usageClient = coreUsageData.getClient(); + const deprecatedApis = http.getRegisteredDeprecatedApis(); + const deprecatedApiUsageStats = await usageClient.getDeprecatedApiUsageStats(); + + return deprecatedApiUsageStats + .filter(({ apiTotalCalls, totalMarkedAsResolved }) => { + return apiTotalCalls > totalMarkedAsResolved; + }) + .filter(({ apiId }) => + deprecatedApis.some((routeDetails) => buildApiDeprecationId(routeDetails) === apiId) + ) + .map((apiUsageStats) => { + const { apiId } = apiUsageStats; + const deprecatedApiDetails = deprecatedApis.find( + (routeDetails) => buildApiDeprecationId(routeDetails) === apiId + ); + if (!deprecatedApiDetails) { + throw new Error(`Unable to find deprecation details for "${apiId}"`); + } + + const { routeAccess } = deprecatedApiDetails; + switch (routeAccess) { + case 'public': { + return buildApiRouteDeprecationDetails({ + apiUsageStats, + deprecatedApiDetails, + }); + } + // if no access is specified then internal is the default + case 'internal': + default: { + return buildApiAccessDeprecationDetails({ + apiUsageStats, + deprecatedApiDetails, + }); + } + } + }); + }; + +export const registerApiDeprecationsInfo = ({ + deprecationsFactory, + http, + coreUsageData, +}: ApiDeprecationsServiceDeps): void => { + const deprecationsRegistery = deprecationsFactory.getRegistry('core.api_deprecations'); + + deprecationsRegistery.registerDeprecations({ + getDeprecations: createGetApiDeprecations({ http, coreUsageData }), + }); +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/i18n_texts.ts similarity index 67% rename from packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts rename to packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/i18n_texts.ts index e52dd1f3d8fd..8cbade658bb2 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/i18n_texts.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/i18n_texts.ts @@ -7,22 +7,26 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { RouterDeprecatedRouteDetails } from '@kbn/core-http-server'; +import { RouterDeprecatedApiDetails } from '@kbn/core-http-server'; import { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; -export const getApiDeprecationTitle = (details: RouterDeprecatedRouteDetails) => { +export const getApiDeprecationTitle = (details: RouterDeprecatedApiDetails) => { const { routePath, routeMethod, routeDeprecationOptions } = details; + if (!routeDeprecationOptions) { + throw new Error(`Router "deprecated" param is missing for path "${routePath}".`); + } + const deprecationType = routeDeprecationOptions.reason.type; const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; - const deprecationTypeText = i18n.translate('core.deprecations.deprecations.apiDeprecationType', { + const deprecationTypeText = i18n.translate('core.deprecations.apiRouteDeprecation.type', { defaultMessage: '{deprecationType, select, remove {is removed} bump {has a newer version available} migrate {is migrated to a different API} other {is deprecated}}', values: { deprecationType }, }); - return i18n.translate('core.deprecations.deprecations.apiDeprecationInfoTitle', { + return i18n.translate('core.deprecations.apiRouteDeprecation.infoTitle', { defaultMessage: 'The "{routeWithMethod}" route {deprecationTypeText}', values: { routeWithMethod, @@ -32,10 +36,13 @@ export const getApiDeprecationTitle = (details: RouterDeprecatedRouteDetails) => }; export const getApiDeprecationMessage = ( - details: RouterDeprecatedRouteDetails, + details: RouterDeprecatedApiDetails, apiUsageStats: CoreDeprecatedApiUsageStats ): string[] => { const { routePath, routeMethod, routeDeprecationOptions } = details; + if (!routeDeprecationOptions) { + throw new Error(`Router "deprecated" param is missing for path "${routePath}".`); + } const { apiLastCalledAt, apiTotalCalls, markedAsResolvedLastCalledAt, totalMarkedAsResolved } = apiUsageStats; @@ -44,7 +51,7 @@ export const getApiDeprecationMessage = ( const routeWithMethod = `${routeMethod.toUpperCase()} ${routePath}`; const messages = [ - i18n.translate('core.deprecations.deprecations.apiDeprecationApiCallsDetailsMessage', { + i18n.translate('core.deprecations.apiRouteDeprecation.apiCallsDetailsMessage', { defaultMessage: 'The API "{routeWithMethod}" has been called {apiTotalCalls} times. The last call was on {apiLastCalledAt}.', values: { @@ -57,17 +64,14 @@ export const getApiDeprecationMessage = ( if (wasResolvedBefore) { messages.push( - i18n.translate( - 'core.deprecations.deprecations.apiDeprecationPreviouslyMarkedAsResolvedMessage', - { - defaultMessage: - 'This issue has been marked as resolved on {markedAsResolvedLastCalledAt} but the API has been called {timeSinceLastResolved, plural, one {# time} other {# times}} since.', - values: { - timeSinceLastResolved: diff, - markedAsResolvedLastCalledAt: moment(markedAsResolvedLastCalledAt).format('LLLL Z'), - }, - } - ) + i18n.translate('core.deprecations.apiRouteDeprecation.previouslyMarkedAsResolvedMessage', { + defaultMessage: + 'This issue has been marked as resolved on {markedAsResolvedLastCalledAt} but the API has been called {timeSinceLastResolved, plural, one {# time} other {# times}} since.', + values: { + timeSinceLastResolved: diff, + markedAsResolvedLastCalledAt: moment(markedAsResolvedLastCalledAt).format('LLLL Z'), + }, + }) ); } @@ -79,12 +83,16 @@ export const getApiDeprecationMessage = ( return messages; }; -export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDetails): string[] => { - const { routeDeprecationOptions } = details; +export const getApiDeprecationsManualSteps = (details: RouterDeprecatedApiDetails): string[] => { + const { routePath, routeDeprecationOptions } = details; + if (!routeDeprecationOptions) { + throw new Error(`Router "deprecated" param is missing for path "${routePath}".`); + } + const deprecationType = routeDeprecationOptions.reason.type; const manualSteps = [ - i18n.translate('core.deprecations.deprecations.manualSteps.apiIseprecatedStep', { + i18n.translate('core.deprecations.apiRouteDeprecation.manualSteps.identifyCallsOriginStep', { defaultMessage: 'Identify the origin of these API calls.', }), ]; @@ -93,7 +101,7 @@ export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDeta case 'bump': { const { newApiVersion } = routeDeprecationOptions.reason; manualSteps.push( - i18n.translate('core.deprecations.deprecations.manualSteps.bumpDetailsStep', { + i18n.translate('core.deprecations.apiRouteDeprecation.manualSteps.bumpTypeStep', { defaultMessage: 'Update the requests to use the following new version of the API instead: "{newApiVersion}".', values: { newApiVersion }, @@ -104,7 +112,7 @@ export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDeta case 'remove': { manualSteps.push( - i18n.translate('core.deprecations.deprecations.manualSteps.removeTypeExplainationStep', { + i18n.translate('core.deprecations.apiRouteDeprecation.manualSteps.removeTypeStep', { defaultMessage: 'This API no longer exists and no replacement is available. Delete any requests you have that use this API.', }) @@ -113,7 +121,7 @@ export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDeta } case 'deprecate': { manualSteps.push( - i18n.translate('core.deprecations.deprecations.manualSteps.removeTypeExplainationStep', { + i18n.translate('core.deprecations.apiRouteDeprecation.manualSteps.deprecateTypeStep', { defaultMessage: 'For now, the API will still work, but will be moved or removed in a future version. Check the Learn more link for more information. If you are no longer using the API, you can mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.', }) @@ -125,7 +133,7 @@ export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDeta const newRouteWithMethod = `${newApiMethod.toUpperCase()} ${newApiPath}`; manualSteps.push( - i18n.translate('core.deprecations.deprecations.manualSteps.migrateDetailsStep', { + i18n.translate('core.deprecations.apiRouteDeprecation.manualSteps.migrateTypeStep', { defaultMessage: 'Update the requests to use the following new API instead: "{newRouteWithMethod}".', values: { newRouteWithMethod }, @@ -137,10 +145,13 @@ export const getApiDeprecationsManualSteps = (details: RouterDeprecatedRouteDeta if (deprecationType !== 'deprecate') { manualSteps.push( - i18n.translate('core.deprecations.deprecations.manualSteps.markAsResolvedStep', { - defaultMessage: - 'Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.', - }) + i18n.translate( + 'core.deprecations.apiRouteDeprecation.manualSteps.routeDepractionMarkAsResolvedStep', + { + defaultMessage: + 'Check that you are no longer using the old API in any requests, and mark this issue as resolved. It will no longer appear in the Upgrade Assistant unless another call using this API is detected.', + } + ) ); } diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/index.ts new file mode 100644 index 000000000000..9caf06c7def8 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { buildApiRouteDeprecationDetails, getIsRouteApiDeprecation } from './route_deprecations'; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/route_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/route_deprecations.ts new file mode 100644 index 000000000000..6f9bab78dff9 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/route/route_deprecations.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { + ApiDeprecationDetails, + DomainDeprecationDetails, +} from '@kbn/core-deprecations-common'; +import _ from 'lodash'; +import type { PostValidationMetadata } from '@kbn/core-http-server'; +import { + getApiDeprecationMessage, + getApiDeprecationsManualSteps, + getApiDeprecationTitle, +} from './i18n_texts'; +import type { BuildApiDeprecationDetailsParams } from '../types'; + +export const getIsRouteApiDeprecation = ({ + isInternalApiRequest, + deprecated, +}: PostValidationMetadata): boolean => { + const hasDeprecatedObject = deprecated && _.isObject(deprecated); + const isNotInternalRequest = !isInternalApiRequest; + + return !!(hasDeprecatedObject && isNotInternalRequest); +}; + +export const buildApiRouteDeprecationDetails = ({ + apiUsageStats, + deprecatedApiDetails, +}: BuildApiDeprecationDetailsParams): DomainDeprecationDetails => { + const { apiId, apiTotalCalls, totalMarkedAsResolved } = apiUsageStats; + const { routeVersion, routePath, routeDeprecationOptions, routeMethod } = deprecatedApiDetails; + if (!routeDeprecationOptions) { + throw new Error(`Expecing deprecated to be defined for route ${apiId}`); + } + + const deprecationLevel = routeDeprecationOptions.severity || 'warning'; + + return { + apiId, + title: getApiDeprecationTitle(deprecatedApiDetails), + level: deprecationLevel, + message: getApiDeprecationMessage(deprecatedApiDetails, apiUsageStats), + documentationUrl: routeDeprecationOptions.documentationUrl, + correctiveActions: { + manualSteps: getApiDeprecationsManualSteps(deprecatedApiDetails), + mark_as_resolved_api: { + routePath, + routeMethod, + routeVersion, + apiTotalCalls, + totalMarkedAsResolved, + timestamp: new Date(), + }, + }, + deprecationType: 'api', + domainId: 'core.http.routes-deprecations', + }; +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/types.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/types.ts new file mode 100644 index 000000000000..ad6436b70c55 --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/api_deprecations/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; +import type { RouterDeprecatedApiDetails } from '@kbn/core-http-server'; +import type { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server'; +import type { DeprecationsFactory } from '../../deprecations_factory'; + +export interface ApiDeprecationsServiceDeps { + deprecationsFactory: DeprecationsFactory; + http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; +} + +export interface BuildApiDeprecationDetailsParams { + apiUsageStats: CoreDeprecatedApiUsageStats; + deprecatedApiDetails: RouterDeprecatedApiDetails; +} diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts index aecf3d5b299a..21e4f801ca98 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations/index.ts @@ -7,5 +7,10 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { buildApiDeprecationId, registerApiDeprecationsInfo } from './api_deprecations'; +export { + buildApiDeprecationId, + registerApiDeprecationsInfo, + getIsAccessApiDeprecation, + getIsRouteApiDeprecation, +} from './api_deprecations'; export { registerConfigDeprecationsInfo } from './config_deprecations'; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts index 39c299d98053..0ea283b6eb5d 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.test.ts @@ -52,7 +52,10 @@ describe('DeprecationsService', () => { expect(http.createRouter).toBeCalledWith('/api/deprecations'); // registers get route '/' expect(router.get).toHaveBeenCalledTimes(1); - expect(router.get).toHaveBeenCalledWith({ path: '/', validate: false }, expect.any(Function)); + expect(router.get).toHaveBeenCalledWith( + { options: { access: 'public' }, path: '/', validate: false }, + expect.any(Function) + ); }); it('calls registerConfigDeprecationsInfo', async () => { diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts index ed3cd061b633..42a2af4ff7d1 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts @@ -14,6 +14,9 @@ export const registerGetRoute = (router: InternalDeprecationRouter) => { router.get( { path: '/', + options: { + access: 'public', + }, validate: false, }, async (context, req, res) => { diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts index 6719f8b99ff5..20f680d4c313 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts @@ -10,9 +10,9 @@ import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; import type { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; -import { isObject } from 'lodash'; -import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; // shouldn't use deep imports +import type { PostValidationMetadata } from '@kbn/core-http-server'; import { buildApiDeprecationId } from '../deprecations'; +import { getIsRouteApiDeprecation, getIsAccessApiDeprecation } from '../deprecations'; interface Dependencies { coreUsageData: InternalCoreUsageDataSetup; @@ -35,8 +35,11 @@ export function createRouteDeprecationsHandler({ }: { coreUsageData: InternalCoreUsageDataSetup; }) { - return (req: CoreKibanaRequest, { deprecated }: { deprecated?: RouteDeprecationInfo }) => { - if (deprecated && isObject(deprecated) && req.route.routePath) { + return (req: CoreKibanaRequest, metadata: PostValidationMetadata) => { + const hasRouteDeprecation = getIsRouteApiDeprecation(metadata); + const hasAccessDeprecation = getIsAccessApiDeprecation(metadata); + const isApiDeprecation = hasAccessDeprecation || hasRouteDeprecation; + if (isApiDeprecation && req.route.routePath) { const counterName = buildApiDeprecationId({ routeMethod: req.route.method, routePath: req.route.routePath, diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts index 840bc5ac22d2..14939188ef0f 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts @@ -19,6 +19,9 @@ export const registerMarkAsResolvedRoute = ( router.post( { path: '/mark_as_resolved', + options: { + access: 'internal', + }, validate: { body: schema.object({ domainId: schema.string(), diff --git a/packages/core/http/core-http-router-server-internal/src/request.ts b/packages/core/http/core-http-router-server-internal/src/request.ts index 9f89f1a70bb4..cbccf31fc094 100644 --- a/packages/core/http/core-http-router-server-internal/src/request.ts +++ b/packages/core/http/core-http-router-server-internal/src/request.ts @@ -177,9 +177,14 @@ export class CoreKibanaRequest< this.headers = isRealReq ? deepFreeze({ ...request.headers }) : request.headers; this.isSystemRequest = this.headers['kbn-system-request'] === 'true'; this.isFakeRequest = !isRealReq; + // set to false if elasticInternalOrigin is explicitly set to false + // otherwise check for the header or the query param this.isInternalApiRequest = - X_ELASTIC_INTERNAL_ORIGIN_REQUEST in this.headers || - Boolean(this.url?.searchParams?.has(ELASTIC_INTERNAL_ORIGIN_QUERY_PARAM)); + this.url?.searchParams?.get(ELASTIC_INTERNAL_ORIGIN_QUERY_PARAM) === 'false' + ? false + : X_ELASTIC_INTERNAL_ORIGIN_REQUEST in this.headers || + this.url?.searchParams?.has(ELASTIC_INTERNAL_ORIGIN_QUERY_PARAM); + // prevent Symbol exposure via Object.getOwnPropertySymbols() Object.defineProperty(this, requestSymbol, { value: request, diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index e7ad85fcda33..c2ea09605c4e 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -28,12 +28,12 @@ import type { VersionedRouter, RouteRegistrar, RouteSecurity, + PostValidationMetadata, } from '@kbn/core-http-server'; import { isZod } from '@kbn/zod'; import { validBodyOutput, getRequestValidation } from '@kbn/core-http-server'; import type { RouteSecurityGetter } from '@kbn/core-http-server'; import type { DeepPartial } from '@kbn/utility-types'; -import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; import { RouteValidator } from './validator'; import { ALLOWED_PUBLIC_VERSION, CoreVersionedRouter } from './versioned_router'; import { CoreKibanaRequest } from './request'; @@ -287,10 +287,13 @@ export class Router { const postValidate: RouterEvents = 'onPostValidate'; - Router.ee.emit(postValidate, request, routeOptions); + Router.ee.emit(postValidate, request, postValidateConext); }; private async handle({ @@ -304,7 +307,7 @@ export class Router void; + onPostValidation: (req: KibanaRequest, metadata: PostValidationMetadata) => void; }; isPublicUnversionedRoute: boolean; handler: RequestHandlerEnhanced< @@ -337,11 +340,19 @@ export class Router { + const access = route.options.access; if (route.isVersioned === true) { return [...route.handlers.entries()].map(([_, { options }]) => { const deprecated = options.options?.deprecated; - return { route, version: `${options.version}`, deprecated }; + return { route, version: `${options.version}`, deprecated, access }; }); } - return { route, version: undefined, deprecated: route.options.deprecated }; + + return { route, version: undefined, deprecated: route.options.deprecated, access }; }) .flat() - .filter(({ deprecated }) => isObject(deprecated)) - .flatMap(({ route, deprecated, version }) => { + .filter(({ deprecated, access }) => { + const isRouteDeprecation = isObject(deprecated); + const isAccessDeprecation = access === 'internal'; + return isRouteDeprecation || isAccessDeprecation; + }) + .flatMap(({ route, deprecated, version, access }) => { return { routeDeprecationOptions: deprecated!, routeMethod: route.method as RouteMethod, routePath: route.path, routeVersion: version, + routeAccess: access, }; }) ); diff --git a/packages/core/http/core-http-server-internal/src/lifecycle_handlers.test.ts b/packages/core/http/core-http-server-internal/src/lifecycle_handlers.test.ts index c81816470885..28f8c70ebbb1 100644 --- a/packages/core/http/core-http-server-internal/src/lifecycle_handlers.test.ts +++ b/packages/core/http/core-http-server-internal/src/lifecycle_handlers.test.ts @@ -417,6 +417,33 @@ describe('restrictInternal post-auth handler', () => { const request = createForgeRequest('public', { 'x-elastic-internal-origin': 'Kibana' }); createForwardSuccess(handler, request); }); + + it('overrides internal api when elasticInternalOrigin=false is set explicitly', () => { + const handler = createRestrictInternalRoutesPostAuthHandler( + { ...config, restrictInternalApis: true }, + logger + ); + + // Will be treated as external + const request = createForgeRequest( + 'internal', + { 'x-elastic-internal-origin': 'Kibana' }, + { elasticInternalOrigin: 'false' } + ); + + responseFactory.badRequest.mockReturnValue('badRequest' as any); + + const result = handler(request, responseFactory, toolkit); + + expect(toolkit.next).not.toHaveBeenCalled(); + expect(responseFactory.badRequest).toHaveBeenCalledTimes(1); + expect(responseFactory.badRequest.mock.calls[0][0]).toMatchInlineSnapshot(` + Object { + "body": "uri [/internal/some-path] with method [get] exists but is not available with the current configuration", + } + `); + expect(result).toEqual('badRequest'); + }); }); describe('customHeaders pre-response handler', () => { diff --git a/packages/core/http/core-http-server-internal/src/types.ts b/packages/core/http/core-http-server-internal/src/types.ts index 0706af9ad73a..c0443e5bc049 100644 --- a/packages/core/http/core-http-server-internal/src/types.ts +++ b/packages/core/http/core-http-server-internal/src/types.ts @@ -16,10 +16,10 @@ import type { IContextContainer, HttpServiceSetup, HttpServiceStart, - RouterDeprecatedRouteDetails, + RouterDeprecatedApiDetails, } from '@kbn/core-http-server'; -import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; -import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route'; +import type { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; +import type { PostValidationMetadata } from '@kbn/core-http-server'; import type { HttpServerSetup } from './http_server'; import type { ExternalUrlConfig } from './external_url'; import type { InternalStaticAssets } from './static_assets'; @@ -58,7 +58,7 @@ export interface InternalHttpServiceSetup plugin?: PluginOpaqueId ) => IRouter; registerOnPostValidation( - cb: (req: CoreKibanaRequest, metadata: { deprecated: RouteDeprecationInfo }) => void + cb: (req: CoreKibanaRequest, metadata: PostValidationMetadata) => void ): void; registerRouterAfterListening: (router: IRouter) => void; registerStaticDir: (path: string, dirPath: string) => void; @@ -71,7 +71,7 @@ export interface InternalHttpServiceSetup contextName: ContextName, provider: IContextProvider ) => IContextContainer; - getRegisteredDeprecatedApis: () => RouterDeprecatedRouteDetails[]; + getRegisteredDeprecatedApis: () => RouterDeprecatedApiDetails[]; } /** @internal */ diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index d2c4cfb5c16f..7b79dfe313bd 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -93,7 +93,9 @@ export type { IRouter, RouteRegistrar, RouterRoute, - RouterDeprecatedRouteDetails, + RouterDeprecatedApiDetails, + RouterAccessDeprecatedApiDetails, + RouterRouteDeprecatedApiDetails, IKibanaSocket, KibanaErrorResponseFactory, KibanaRedirectionResponseFactory, @@ -121,6 +123,7 @@ export type { RouteSecurityGetter, InternalRouteSecurity, RouteDeprecationInfo, + PostValidationMetadata, } from './src/router'; export { validBodyOutput, diff --git a/packages/core/http/core-http-server/src/http_contract.ts b/packages/core/http/core-http-server/src/http_contract.ts index e2f675bd8d0c..2da699e5ea4a 100644 --- a/packages/core/http/core-http-server/src/http_contract.ts +++ b/packages/core/http/core-http-server/src/http_contract.ts @@ -12,7 +12,7 @@ import type { IContextProvider, IRouter, RequestHandlerContextBase, - RouterDeprecatedRouteDetails, + RouterDeprecatedApiDetails, } from './router'; import type { AuthenticationHandler, @@ -362,12 +362,12 @@ export interface HttpServiceSetup< getServerInfo: () => HttpServerInfo; /** - * Provides a list of all registered deprecated routes {{@link RouterDeprecatedRouteDetails | information}}. + * Provides a list of all registered deprecated routes {{@link RouterDeprecatedApiDetails | information}}. * The routers will be evaluated everytime this function gets called to * accommodate for any late route registrations - * @returns {RouterDeprecatedRouteDetails[]} + * @returns {RouterDeprecatedApiDetails[]} */ - getDeprecatedRoutes: () => RouterDeprecatedRouteDetails[]; + getDeprecatedRoutes: () => RouterDeprecatedApiDetails[]; } /** @public */ diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index 30a1b6a5c9cc..166fcad32495 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -65,6 +65,7 @@ export type { Privilege, PrivilegeSet, RouteDeprecationInfo, + PostValidationMetadata, } from './route'; export { validBodyOutput, ReservedPrivilegesSet } from './route'; @@ -81,7 +82,14 @@ export type { LazyValidator, } from './route_validator'; export { RouteValidationError } from './route_validator'; -export type { IRouter, RouteRegistrar, RouterRoute, RouterDeprecatedRouteDetails } from './router'; +export type { + IRouter, + RouteRegistrar, + RouterRoute, + RouterDeprecatedApiDetails, + RouterAccessDeprecatedApiDetails, + RouterRouteDeprecatedApiDetails, +} from './router'; export type { IKibanaSocket } from './socket'; export type { KibanaErrorResponseFactory, diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index eec9a01f6056..da24adcb6802 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -525,3 +525,12 @@ export interface RouteConfig { */ options?: RouteConfigOptions; } + +/** + * Post Validation Route emitter metadata. + */ +export interface PostValidationMetadata { + deprecated?: RouteDeprecationInfo; + isInternalApiRequest: boolean; + isPublicAccess: boolean; +} diff --git a/packages/core/http/core-http-server/src/router/router.ts b/packages/core/http/core-http-server/src/router/router.ts index d8b79bee1302..f770cfcc45e4 100644 --- a/packages/core/http/core-http-server/src/router/router.ts +++ b/packages/core/http/core-http-server/src/router/router.ts @@ -10,7 +10,7 @@ import type { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi'; import type Boom from '@hapi/boom'; import type { VersionedRouter } from '../versioning'; -import type { RouteConfig, RouteDeprecationInfo, RouteMethod } from './route'; +import type { RouteAccess, RouteConfig, RouteDeprecationInfo, RouteMethod } from './route'; import type { RequestHandler, RequestHandlerWrapper } from './request_handler'; import type { RequestHandlerContextBase } from './request_handler_context'; import type { RouteConfigOptions } from './route'; @@ -143,9 +143,22 @@ export interface RouterRoute { } /** @public */ -export interface RouterDeprecatedRouteDetails { - routeDeprecationOptions: RouteDeprecationInfo; +export interface RouterDeprecatedApiDetails { + routeDeprecationOptions?: RouteDeprecationInfo; routeMethod: RouteMethod; routePath: string; routeVersion?: string; + routeAccess?: RouteAccess; +} + +/** @public */ +export interface RouterRouteDeprecatedApiDetails extends RouterDeprecatedApiDetails { + routeAccess: 'public'; + routeDeprecationOptions: RouteDeprecationInfo; +} + +/** @public */ +export interface RouterAccessDeprecatedApiDetails extends RouterDeprecatedApiDetails { + routeAccess: 'internal'; + routeDeprecationOptions?: RouteDeprecationInfo; } diff --git a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts index 39df9d30d19c..e713e3e905a4 100644 --- a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts +++ b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts @@ -145,6 +145,9 @@ export interface CoreUsageStats { 'savedObjectsRepository.resolvedOutcome.conflict'?: number; 'savedObjectsRepository.resolvedOutcome.notFound'?: number; 'savedObjectsRepository.resolvedOutcome.total'?: number; + // API Deprecations counters + 'deprecated_api_calls_resolved.total'?: number; + 'deprecated_api_calls.total'?: number; } /** diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts index b969215f508d..41e7bf9fd740 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts @@ -1183,6 +1183,18 @@ export function getCoreUsageCollector( 'How many times a saved object has resolved with any of the four possible outcomes.', }, }, + 'deprecated_api_calls_resolved.total': { + type: 'integer', + _meta: { + description: 'How many times deprecated APIs has been marked as resolved', + }, + }, + 'deprecated_api_calls.total': { + type: 'integer', + _meta: { + description: 'How many times deprecated APIs has been called.', + }, + }, }, fetch() { return getCoreUsageDataService().getCoreUsageData(); diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index aed47fa83964..6a4317749254 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -9290,6 +9290,18 @@ "_meta": { "description": "How many times a saved object has resolved with any of the four possible outcomes." } + }, + "deprecated_api_calls_resolved.total": { + "type": "integer", + "_meta": { + "description": "How many times deprecated APIs has been marked as resolved" + } + }, + "deprecated_api_calls.total": { + "type": "integer", + "_meta": { + "description": "How many times deprecated APIs has been called." + } } } }, @@ -10887,12 +10899,6 @@ "description": "Non-default value of setting." } }, - "observability:newLogsOverview": { - "type": "boolean", - "_meta": { - "description": "Enable the new logs overview component." - } - }, "observability:searchExcludedDataTiers": { "type": "array", "items": { @@ -10901,6 +10907,12 @@ "description": "Non-default value of setting." } } + }, + "observability:newLogsOverview": { + "type": "boolean", + "_meta": { + "description": "Enable the new logs overview component." + } } } }, diff --git a/x-pack/plugins/upgrade_assistant/README.md b/x-pack/plugins/upgrade_assistant/README.md index 9e2cf1b47e60..531d4c1702c0 100644 --- a/x-pack/plugins/upgrade_assistant/README.md +++ b/x-pack/plugins/upgrade_assistant/README.md @@ -21,14 +21,14 @@ When we want to enable ML model snapshot deprecation warnings again we need to c There are three sources of deprecation information: -* [**Elasticsearch Deprecation Info API.**](https://www.elastic.co/guide/en/elasticsearch/reference/master/migration-api-deprecation.html) +* [**Elasticsearch Deprecation Info API.**](https://www.elastic.co/guide/en/elasticsearch/reference/main/migration-api-deprecation.html) This is information about Elasticsearch cluster, node, Machine Learning, and index-level settings that use deprecated features that will be removed or changed in the next major version. ES server engineers are responsible for adding deprecations to the Deprecation Info API. * [**Elasticsearch deprecation logs.**](https://www.elastic.co/guide/en/elasticsearch/reference/current/logging.html#deprecation-logging) These surface runtime deprecations, e.g. a Painless script that uses a deprecated accessor or a request to a deprecated API. These are also generally surfaced as deprecation headers within the response. Even if the cluster state is good, app maintainers need to watch the logs in case deprecations are discovered as data is migrated. Starting in 7.x, deprecation logs can be written to a file or a data stream ([#58924](https://github.com/elastic/elasticsearch/pull/58924)). When the data stream exists, the Upgrade Assistant provides a way to analyze the logs through Observability or Discover ([#106521](https://github.com/elastic/kibana/pull/106521)). -* [**Kibana deprecations API.**](https://github.com/elastic/kibana/blob/master/src/core/server/deprecations/README.mdx) This is information about deprecated features and configs in Kibana. These deprecations are only communicated to the user if the deployment is using these features. Kibana engineers are responsible for adding deprecations to the deprecations API for their respective team. +* [**Kibana deprecations API.**](https://github.com/elastic/kibana/blob/main/src/core/server/deprecations/README.mdx) This is information about deprecated features and configs in Kibana. These deprecations are only communicated to the user if the deployment is using these features. Kibana engineers are responsible for adding deprecations to the deprecations API for their respective team. ### Fixing problems @@ -284,23 +284,27 @@ yarn start --plugin-path=examples/routing_example --plugin-path=examples/develop The following comprehensive deprecated routes examples are registered inside the folder: `examples/routing_example/server/routes/deprecated_routes` Run them in the console to trigger the deprecation condition so they show up in the UA: +We need to explicitly set the query param `elasticInternalOrigin` to `false` to track the request as non-internal origin. ``` -# Versioned routes: Version 1 is deprecated -GET kbn:/api/routing_example/d/versioned?apiVersion=1 -GET kbn:/api/routing_example/d/versioned?apiVersion=2 - -# Non-versioned routes -GET kbn:/api/routing_example/d/removed_route -GET kbn:/api/routing_example/d/deprecated_route -POST kbn:/api/routing_example/d/migrated_route +# Route deprecations for Versioned routes +GET kbn:/api/routing_example/d/versioned_route?apiVersion=2023-10-31&elasticInternalOrigin=false + +# Route deprecations for Non-versioned routes +GET kbn:/api/routing_example/d/removed_route?elasticInternalOrigin=false +GET kbn:/api/routing_example/d/deprecated_route?elasticInternalOrigin=false +POST kbn:/api/routing_example/d/migrated_route?elasticInternalOrigin=false {} + +# Access deprecations +GET kbn:/api/routing_example/d/internal_deprecated_route?elasticInternalOrigin=false +GET kbn:/internal/routing_example/d/internal_only_route?elasticInternalOrigin=false +GET kbn:/internal/routing_example/d/internal_versioned_route?apiVersion=1&elasticInternalOrigin=false ``` 1. You can also mark as deprecated in the UA to remove the deprecation from the list. 2. Check the telemetry response to see the reported data about the deprecated route. -3. Calling version 2 of the API does not do anything since it is not deprecated unlike version `1` (`GET kbn:/api/routing_example/d/versioned?apiVersion=2`) -4. Internally you can see the deprecations counters from the dev console by running the following: +3. Internally you can see the deprecations counters from the dev console by running the following: ``` GET .kibana_usage_counters/_search { @@ -331,7 +335,7 @@ This is a non-exhaustive list of different error scenarios in Upgrade Assistant. ### Telemetry -The Upgrade Assistant tracks several triggered events in the UI, using Kibana Usage Collection service's [UI counters](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#ui-counters). +The Upgrade Assistant tracks several triggered events in the UI, using Kibana Usage Collection service's [UI counters](https://github.com/elastic/kibana/blob/main/src/plugins/usage_collection/README.mdx#ui-counters). **Overview page** - Component loaded @@ -350,6 +354,6 @@ The Upgrade Assistant tracks several triggered events in the UI, using Kibana Us - Component loaded - Click event for "Quick resolve" button -In addition to UI counters, the Upgrade Assistant has a [custom usage collector](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#custom-collector). It currently is only responsible for tracking whether the user has deprecation logging enabled or not. +In addition to UI counters, the Upgrade Assistant has a [custom usage collector](https://github.com/elastic/kibana/blob/main/src/plugins/usage_collection/README.mdx#custom-collector). It currently is only responsible for tracking whether the user has deprecation logging enabled or not. -For testing instructions, refer to the [Kibana Usage Collection service README](https://github.com/elastic/kibana/blob/master/src/plugins/usage_collection/README.mdx#testing). \ No newline at end of file +For testing instructions, refer to the [Kibana Usage Collection service README](https://github.com/elastic/kibana/blob/main/src/plugins/usage_collection/README.mdx#testing). \ No newline at end of file diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts index f146bf38f5f2..bbce39e9fb29 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts @@ -7,21 +7,19 @@ import expect from '@kbn/expect'; import { expect as expectExpect } from 'expect'; -import type { DomainDeprecationDetails } from '@kbn/core-deprecations-common'; -import { ApiDeprecationDetails } from '@kbn/core-deprecations-common/src/types'; import { setTimeout as setTimeoutAsync } from 'timers/promises'; import { UsageCountersSavedObject } from '@kbn/usage-collection-plugin/server'; import _ from 'lodash'; +import type { + ApiDeprecationDetails, + DomainDeprecationDetails, +} from '@kbn/core-deprecations-common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; -interface DomainApiDeprecationDetails extends ApiDeprecationDetails { - domainId: string; -} - const getApiDeprecations = (allDeprecations: DomainDeprecationDetails[]) => { return allDeprecations.filter( (deprecation) => deprecation.deprecationType === 'api' - ) as unknown as DomainApiDeprecationDetails[]; + ) as unknown as Array>; }; export default function ({ getService }: FtrProviderContext) { @@ -30,7 +28,10 @@ export default function ({ getService }: FtrProviderContext) { const retry = getService('retry'); const es = getService('es'); - describe('Kibana API Deprecations', () => { + describe('Kibana API Deprecations', function () { + // bail on first error in this suite since cases sequentially depend on each other + this.bail(true); + before(async () => { // await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.emptyKibanaIndex(); @@ -42,6 +43,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns deprecated APIs when the api is called', async () => { + await supertest + .get(`/internal/routing_example/d/internal_versioned_route?apiVersion=1`) + .expect(200); await supertest.get(`/api/routing_example/d/removed_route`).expect(200); // sleep a little until the usage counter is synced into ES @@ -51,7 +55,7 @@ export default function ({ getService }: FtrProviderContext) { async () => { const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; const apiDeprecations = getApiDeprecations(deprecations); - expect(apiDeprecations.length).to.equal(1); + expect(apiDeprecations.length).to.equal(2); expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({ routePath: '/api/routing_example/d/removed_route', @@ -68,6 +72,23 @@ export default function ({ getService }: FtrProviderContext) { expectExpect(apiDeprecations[0].title).toEqual( 'The "GET /api/routing_example/d/removed_route" route is removed' ); + + expectExpect(apiDeprecations[1].correctiveActions.mark_as_resolved_api).toEqual({ + routePath: '/internal/routing_example/d/internal_versioned_route', + routeMethod: 'get', + routeVersion: '1', + apiTotalCalls: 1, + totalMarkedAsResolved: 0, + timestamp: expectExpect.any(String), + }); + + expectExpect(apiDeprecations[1].domainId).toEqual('core.api_deprecations'); + expectExpect(apiDeprecations[1].apiId).toEqual( + '1|get|/internal/routing_example/d/internal_versioned_route' + ); + expectExpect(apiDeprecations[1].title).toEqual( + 'The "GET /internal/routing_example/d/internal_versioned_route" API is internal to Elastic' + ); }, undefined, 2000 @@ -76,7 +97,7 @@ export default function ({ getService }: FtrProviderContext) { it('no longer returns deprecated API when it is marked as resolved', async () => { await supertest - .post(`/api/deprecations/mark_as_resolved`) + .post(`/api/deprecations/mark_as_resolved?elasticInternalOrigin=true`) .set('kbn-xsrf', 'xxx') .send({ domainId: 'core.api_deprecations', @@ -91,7 +112,10 @@ export default function ({ getService }: FtrProviderContext) { await retry.tryForTime(15 * 1000, async () => { const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; const apiDeprecations = getApiDeprecations(deprecations); - expect(apiDeprecations.length).to.equal(0); + expect(apiDeprecations.length).to.equal(1); + expectExpect(apiDeprecations[0].apiId).toEqual( + '1|get|/internal/routing_example/d/internal_versioned_route' + ); }); }); @@ -105,7 +129,7 @@ export default function ({ getService }: FtrProviderContext) { async () => { const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; const apiDeprecations = getApiDeprecations(deprecations); - expect(apiDeprecations.length).to.equal(1); + expect(apiDeprecations.length).to.equal(2); expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({ routePath: '/api/routing_example/d/removed_route', @@ -132,31 +156,80 @@ export default function ({ getService }: FtrProviderContext) { }, }); - expect(hits.hits.length).to.equal(3); + expect(hits.hits.length).to.equal(4); const counters = hits.hits.map((hit) => hit._source!['usage-counter']).sort(); - expectExpect(_.sortBy(counters, 'counterType')).toEqual([ - { - count: 1, - counterName: 'unversioned|get|/api/routing_example/d/removed_route', - counterType: 'deprecated_api_call:marked_as_resolved', - domainId: 'core', - source: 'server', - }, - { - count: 1, - counterName: 'unversioned|get|/api/routing_example/d/removed_route', - counterType: 'deprecated_api_call:resolved', - domainId: 'core', - source: 'server', - }, - { - count: 2, - counterName: 'unversioned|get|/api/routing_example/d/removed_route', - counterType: 'deprecated_api_call:total', - domainId: 'core', - source: 'server', - }, - ]); + expectExpect(_.sortBy(counters, 'counterType')).toEqual(expectedSuiteUsageCounters); + }); + + it('Does not increment internal origin calls', async () => { + await supertest + .get(`/api/routing_example/d/removed_route?elasticInternalOrigin=true`) + .expect(200); + // call another deprecated api to make sure that we are not verifying stale results + await supertest + .get(`/api/routing_example/d/versioned_route?apiVersion=2023-10-31`) + .expect(200); + + // sleep a little until the usage counter is synced into ES + await setTimeoutAsync(3000); + await retry.tryForTime(15 * 1000, async () => { + const should = ['total', 'resolved', 'marked_as_resolved'].map((type) => ({ + match: { 'usage-counter.counterType': `deprecated_api_call:${type}` }, + })); + + const { hits } = await es.search<{ 'usage-counter': UsageCountersSavedObject }>({ + index: '.kibana_usage_counters', + body: { + query: { bool: { should } }, + }, + }); + + expect(hits.hits.length).to.equal(5); + const counters = hits.hits.map((hit) => hit._source!['usage-counter']).sort(); + expectExpect(_.sortBy(counters, 'counterType')).toEqual( + [ + ...expectedSuiteUsageCounters, + { + domainId: 'core', + counterName: '2023-10-31|get|/api/routing_example/d/versioned_route', + counterType: 'deprecated_api_call:total', + source: 'server', + count: 1, + }, + ].sort() + ); + }); }); }); } + +const expectedSuiteUsageCounters = [ + { + domainId: 'core', + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:marked_as_resolved', + source: 'server', + count: 1, + }, + { + domainId: 'core', + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:resolved', + source: 'server', + count: 1, + }, + { + domainId: 'core', + counterName: '1|get|/internal/routing_example/d/internal_versioned_route', + counterType: 'deprecated_api_call:total', + source: 'server', + count: 1, + }, + { + domainId: 'core', + counterName: 'unversioned|get|/api/routing_example/d/removed_route', + counterType: 'deprecated_api_call:total', + source: 'server', + count: 2, + }, +]; From be1a4bbe4095079643d8e31d7cc967f6f91aa57a Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:59:36 +0100 Subject: [PATCH 049/100] [ES|QL] `STATS` command APIs (#199322) ## Summary Partially addresses https://github.com/elastic/kibana/issues/191812 This PR implement higher-level convenience methods for working with `STATS` commands: - `commands.stats.list()` - iterates over all `STATS` commands. - `commands.stats.byIndex()` - retrieves the Nth `STATS` command. - `commands.stats.summarize()` - returns summary about fields used in aggregates and grouping for all `STATS` commands in the query. - `commands.stats.summarizeCommand()` - same as `.summarize()`, but returns a summary only about the requested command. Usage: ```ts const query = EsqlQuery.fromSrc('FROM index | STATS a = max(b)'); const summary = commands.stats.summarize(query); // [ { aggregates: { a: { fields: ['b'] }} ] ``` ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) --- .../kbn-esql-ast/src/mutate/commands/index.ts | 3 +- .../src/mutate/commands/stats/index.test.ts | 317 ++++++++++++++++++ .../src/mutate/commands/stats/index.ts | 234 +++++++++++++ packages/kbn-esql-ast/src/query/query.ts | 4 + 4 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 packages/kbn-esql-ast/src/mutate/commands/stats/index.test.ts create mode 100644 packages/kbn-esql-ast/src/mutate/commands/stats/index.ts diff --git a/packages/kbn-esql-ast/src/mutate/commands/index.ts b/packages/kbn-esql-ast/src/mutate/commands/index.ts index 9e2599c49345..8068aa5d3ef9 100644 --- a/packages/kbn-esql-ast/src/mutate/commands/index.ts +++ b/packages/kbn-esql-ast/src/mutate/commands/index.ts @@ -10,5 +10,6 @@ import * as from from './from'; import * as limit from './limit'; import * as sort from './sort'; +import * as stats from './stats'; -export { from, limit, sort }; +export { from, limit, sort, stats }; diff --git a/packages/kbn-esql-ast/src/mutate/commands/stats/index.test.ts b/packages/kbn-esql-ast/src/mutate/commands/stats/index.test.ts new file mode 100644 index 000000000000..6fdafa45e383 --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/commands/stats/index.test.ts @@ -0,0 +1,317 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import * as commands from '..'; +import { EsqlQuery } from '../../../query'; + +describe('commands.stats', () => { + describe('.list()', () => { + it('lists all "STATS" commands', () => { + const src = 'FROM index | LIMIT 1 | STATS agg() | LIMIT 2 | STATS max()'; + const query = EsqlQuery.fromSrc(src); + + const nodes = [...commands.stats.list(query.ast)]; + + expect(nodes).toMatchObject([ + { + type: 'command', + name: 'stats', + args: [ + { + type: 'function', + name: 'agg', + }, + ], + }, + { + type: 'command', + name: 'stats', + args: [ + { + type: 'function', + name: 'max', + }, + ], + }, + ]); + }); + }); + + describe('.byIndex()', () => { + it('retrieves the specific "STATS" command by index', () => { + const src = 'FROM index | LIMIT 1 | STATS agg() | LIMIT 2 | STATS max()'; + const query = EsqlQuery.fromSrc(src); + + const node1 = commands.stats.byIndex(query.ast, 1); + const node2 = commands.stats.byIndex(query.ast, 0); + + expect(node1).toMatchObject({ + type: 'command', + name: 'stats', + args: [ + { + type: 'function', + name: 'max', + }, + ], + }); + expect(node2).toMatchObject({ + type: 'command', + name: 'stats', + args: [ + { + type: 'function', + name: 'agg', + }, + ], + }); + }); + }); + + describe('.summarizeCommand()', () => { + it('returns summary of a simple field, defined through assignment', () => { + const src = 'FROM index | STATS foo = agg(bar)'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary).toMatchObject({ + command, + aggregates: { + foo: { + arg: { + type: 'function', + name: '=', + }, + field: 'foo', + column: { + type: 'column', + name: 'foo', + }, + definition: { + type: 'function', + name: 'agg', + args: [ + { + type: 'column', + name: 'bar', + }, + ], + }, + terminals: [ + { + type: 'column', + name: 'bar', + }, + ], + fields: ['bar'], + }, + }, + }); + }); + + it('can summarize field defined without assignment', () => { + const src = 'FROM index | STATS agg( /* haha 😅 */ max(foo), bar, baz)'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary).toMatchObject({ + command, + aggregates: { + '`agg( /* haha 😅 */ max(foo), bar, baz)`': { + arg: { + type: 'function', + name: 'agg', + }, + field: '`agg( /* haha 😅 */ max(foo), bar, baz)`', + column: { + type: 'column', + name: '`agg( /* haha 😅 */ max(foo), bar, baz)`', + }, + definition: { + type: 'function', + name: 'agg', + }, + terminals: [ + { + type: 'column', + name: 'foo', + }, + { + type: 'column', + name: 'bar', + }, + { + type: 'column', + name: 'baz', + }, + ], + fields: ['foo', 'bar', 'baz'], + }, + }, + }); + }); + + it('returns a map of stats about two fields', () => { + const src = 'FROM index | STATS foo = agg(f1) + agg(f2), a.b = agg(f3)'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary).toMatchObject({ + aggregates: { + foo: { + field: 'foo', + fields: ['f1', 'f2'], + }, + 'a.b': { + field: 'a.b', + fields: ['f3'], + }, + }, + }); + expect(summary.fields).toEqual(new Set(['f1', 'f2', 'f3'])); + }); + + it('can get de-duplicated list of used fields', () => { + const src = 'FROM index | STATS foo = agg(f1) + agg(f2), a.b = agg(f1)'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary.fields).toEqual(new Set(['f1', 'f2'])); + }); + + describe('params', () => { + it('can use params as source field names', () => { + const src = 'FROM index | STATS foo = agg(f1.?aha) + ?aha(?nested.?param), a.b = agg(f1)'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary).toMatchObject({ + aggregates: { + foo: { + fields: ['f1.?aha', '?nested.?param'], + }, + 'a.b': { + fields: ['f1'], + }, + }, + }); + expect(summary.fields).toEqual(new Set(['f1.?aha', '?nested.?param', 'f1'])); + }); + + it('can use params as destination field names', () => { + const src = 'FROM index | STATS ?dest = agg(asdf) BY asdf'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary).toMatchObject({ + aggregates: { + '?dest': { + fields: ['asdf'], + }, + }, + }); + expect(summary.fields).toEqual(new Set(['asdf'])); + }); + }); + + describe('BY option', () => { + it('can collect fields from the BY option', () => { + const src = 'FROM index | STATS max(1) BY abc'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary.aggregates).toEqual({ + '`max(1)`': expect.any(Object), + }); + expect(summary.fields).toEqual(new Set(['abc'])); + }); + + it('returns all "grouping" fields', () => { + const src = 'FROM index | STATS max(1) BY a, b, c'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary.aggregates).toEqual({ + '`max(1)`': expect.any(Object), + }); + expect(summary.grouping).toMatchObject({ + a: { type: 'column' }, + b: { type: 'column' }, + c: { type: 'column' }, + }); + }); + + it('can have params and quoted fields in grouping', () => { + const src = 'FROM index | STATS max(1) BY `a😎`, ?123, a.?b.?0.`😎`'; + const query = EsqlQuery.fromSrc(src); + + const command = commands.stats.byIndex(query.ast, 0)!; + const summary = commands.stats.summarizeCommand(query, command); + + expect(summary.aggregates).toEqual({ + '`max(1)`': expect.any(Object), + }); + expect(summary.grouping).toMatchObject({ + '`a😎`': { type: 'column' }, + // '?123': { type: 'column' }, + 'a.?b.?0.`😎`': { type: 'column' }, + }); + }); + }); + }); + + describe('.summarize()', () => { + it('can summarize multiple stats commands', () => { + const src = 'FROM index | LIMIT 1 | STATS agg() | LIMIT 2 | STATS max(a, b, c), max2(d.e)'; + const query = EsqlQuery.fromSrc(src); + const summary = commands.stats.summarize(query); + + expect(summary).toMatchObject([ + { + aggregates: { + '`agg()`': { + field: '`agg()`', + fields: [], + }, + }, + fields: new Set([]), + }, + { + aggregates: { + '`max(a, b, c)`': { + field: '`max(a, b, c)`', + fields: ['a', 'b', 'c'], + }, + '`max2(d.e)`': { + field: '`max2(d.e)`', + fields: ['d.e'], + }, + }, + fields: new Set(['a', 'b', 'c', 'd.e']), + }, + ]); + }); + }); +}); diff --git a/packages/kbn-esql-ast/src/mutate/commands/stats/index.ts b/packages/kbn-esql-ast/src/mutate/commands/stats/index.ts new file mode 100644 index 000000000000..9a07549c0e0e --- /dev/null +++ b/packages/kbn-esql-ast/src/mutate/commands/stats/index.ts @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Walker } from '../../../walker'; +import { Visitor } from '../../../visitor'; +import { LeafPrinter } from '../../../pretty_print'; +import { Builder } from '../../../builder'; +import { singleItems } from '../../../visitor/utils'; +import type { + ESQLAstQueryExpression, + ESQLColumn, + ESQLCommand, + ESQLList, + ESQLLiteral, + ESQLProperNode, + ESQLTimeInterval, +} from '../../../types'; +import * as generic from '../../generic'; +import { isColumn, isFunctionExpression } from '../../../ast/helpers'; +import type { EsqlQuery } from '../../../query'; + +/** + * Lists all "LIMIT" commands in the query AST. + * + * @param ast The root AST node to search for "LIMIT" commands. + * @returns A collection of "LIMIT" commands. + */ +export const list = (ast: ESQLAstQueryExpression): IterableIterator => { + return generic.commands.list(ast, (cmd) => cmd.name === 'stats'); +}; + +/** + * Retrieves the "LIMIT" command at the specified index in order of appearance. + * + * @param ast The root AST node to search for "LIMIT" commands. + * @param index The index of the "LIMIT" command to retrieve. + * @returns The "LIMIT" command at the specified index, if any. + */ +export const byIndex = (ast: ESQLAstQueryExpression, index: number): ESQLCommand | undefined => { + return [...list(ast)][index]; +}; + +/** + * Summary of a STATS command. + */ +export interface StatsCommandSummary { + /** + * The "STATS" command AST node from which this summary was produced. + */ + command: ESQLCommand; + + /** + * Summary of the main arguments of the "STATS" command. + */ + aggregates: Record; + + /** + * Summary of the "BY" arguments of the "STATS" command. + */ + grouping: Record; + + /** + * De-duplicated list all of ES|QL-syntax formatted field names from the + * {@link aggregates} and {@link grouping} fields. + */ + fields: Set; +} + +/** + * Summary of STATS command "aggregates" section (main arguments). + * + * STATS [ BY ] + */ +export interface StatsAggregatesSummary { + /** + * STATS command argument AST node (as was parsed). + */ + arg: ESQLProperNode; + + /** + * The field name, correctly formatted, extracted from the AST. + */ + field: string; + + /** + * A `column` AST node, which represents the field name. If no column AST node + * was found, a new one "virtual" column node is created. + */ + column: ESQLColumn; + + /** + * The definition of the field, which is the right-hand side of the `=` + * operator, or the argument itself if no `=` operator is present. + */ + definition: ESQLProperNode; + + /** + * A list of terminal nodes that were found in the definition. + */ + terminals: Array; + + /** + * Correctly formatted list of field names that were found in the {@link terminals}. + */ + fields: string[]; +} + +const summarizeArgParts = ( + query: EsqlQuery, + arg: ESQLProperNode +): [column: ESQLColumn, definition: ESQLProperNode] => { + if (isFunctionExpression(arg) && arg.name === '=' && isColumn(arg.args[0])) { + const [column, definition] = singleItems(arg.args); + + return [column as ESQLColumn, definition as ESQLProperNode]; + } + + const name = [...query.src].slice(arg.location.min, arg.location.max + 1).join(''); + const args = [Builder.identifier({ name })]; + const column = Builder.expression.column({ args }); + + return [column, arg]; +}; + +const summarizeArg = (query: EsqlQuery, arg: ESQLProperNode): StatsAggregatesSummary => { + const [column, definition] = summarizeArgParts(query, arg); + const terminals: StatsAggregatesSummary['terminals'] = []; + const fields: StatsAggregatesSummary['fields'] = []; + + Walker.walk(definition, { + visitLiteral(node) { + terminals.push(node); + }, + visitColumn(node) { + terminals.push(node); + fields.push(LeafPrinter.column(node)); + }, + visitListLiteral(node) { + terminals.push(node); + }, + visitTimeIntervalLiteral(node) { + terminals.push(node); + }, + }); + + const summary: StatsAggregatesSummary = { + arg, + field: LeafPrinter.column(column), + column, + definition, + terminals, + fields, + }; + + return summary; +}; + +/** + * Returns a summary of the STATS command. + * + * @param query Query which contains the AST and source code. + * @param command The STATS command AST node to summarize. + * @returns Summary of the STATS command. + */ +export const summarizeCommand = (query: EsqlQuery, command: ESQLCommand): StatsCommandSummary => { + const aggregates: StatsCommandSummary['aggregates'] = {}; + const grouping: StatsCommandSummary['grouping'] = {}; + const fields: StatsCommandSummary['fields'] = new Set(); + + // Process main arguments, the "aggregates" part of the command. + new Visitor() + .on('visitExpression', (ctx) => { + const summary = summarizeArg(query, ctx.node); + aggregates[summary.field] = summary; + for (const field of summary.fields) fields.add(field); + }) + .on('visitCommand', () => {}) + .on('visitStatsCommand', (ctx) => { + for (const _ of ctx.visitArguments()); + }) + .visitCommand(command); + + // Process the "BY" arguments, the "grouping" part of the command. + new Visitor() + .on('visitExpression', () => {}) + .on('visitColumnExpression', (ctx) => { + const column = ctx.node; + const formatted = LeafPrinter.column(column); + grouping[formatted] = column; + fields.add(formatted); + }) + .on('visitCommandOption', (ctx) => { + if (ctx.node.name !== 'by') return; + for (const _ of ctx.visitArguments()); + }) + .on('visitCommand', () => {}) + .on('visitStatsCommand', (ctx) => { + for (const _ of ctx.visitOptions()); + }) + .visitCommand(command); + + const summary: StatsCommandSummary = { + command, + aggregates, + grouping, + fields, + }; + + return summary; +}; + +/** + * Summarizes all STATS commands in the query. + * + * @param query Query to summarize. + * @returns Returns a list of summaries for all STATS commands in the query in + * order of appearance. + */ +export const summarize = (query: EsqlQuery): StatsCommandSummary[] => { + const summaries: StatsCommandSummary[] = []; + + for (const command of list(query.ast)) { + const summary = summarizeCommand(query, command); + summaries.push(summary); + } + + return summaries; +}; diff --git a/packages/kbn-esql-ast/src/query/query.ts b/packages/kbn-esql-ast/src/query/query.ts index 60435cc64c97..66c9fd58df08 100644 --- a/packages/kbn-esql-ast/src/query/query.ts +++ b/packages/kbn-esql-ast/src/query/query.ts @@ -15,6 +15,10 @@ import { WrappingPrettyPrinterOptions, } from '../pretty_print/wrapping_pretty_printer'; +/** + * Represents a parsed or programmatically created ES|QL query. Keeps track of + * the AST, source code, and optionally lexer tokens. + */ export class EsqlQuery { public static readonly fromSrc = (src: string, opts?: ParseOptions): EsqlQuery => { const { root, tokens } = parse(src, opts); From 13ae98602f1979bab413cefe57e3685510adc3d3 Mon Sep 17 00:00:00 2001 From: Ania Kowalska <63072419+akowalska622@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:01:16 +0100 Subject: [PATCH 050/100] [Discover] fix: responsive data view picker (#199617) ## Summary Closes https://github.com/elastic/kibana/issues/199434 `ChangeDataView` had two problems on smaller screens: 1. The `Data view` label was wrapped across two rows, causing the parent container to expand and misalign with the picker. 2. The picker container was overflowing, and the text was not truncated. ![image](https://github.com/user-attachments/assets/1eeb5cf2-bcae-4a1d-b28c-13c5c508b4c1) Setting `min-width: 0` on two parent containers solved the problem: Screenshot 2024-11-11 at 11 52 09 ![data-view-picker](https://github.com/user-attachments/assets/08e23d5a-7f09-4530-aca5-bac7fa0da7cd) ### Checklist Delete any items that are not applicable to this PR. - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels) - [ ] This will appear in the **Release Notes** and follow the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../public/dataview_picker/change_dataview.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx index ade754004b8e..d89621d59867 100644 --- a/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx +++ b/src/plugins/unified_search/public/dataview_picker/change_dataview.tsx @@ -43,6 +43,10 @@ const mapAdHocDataView = (adHocDataView: DataView): DataViewListItemEnhanced => }; }; +const shrinkableContainerCss = css` + min-width: 0; +`; + export function ChangeDataView({ isMissingCurrent, currentDataViewId, @@ -238,7 +242,7 @@ export function ChangeDataView({ return ( <> - + - + Date: Tue, 12 Nov 2024 13:01:36 +0100 Subject: [PATCH 051/100] [Custom threshold] Fix the custom equation label on the preview lens chart (#199618) Fixes #181876 ## Summary This PR fixes the custom equation label on the preview lens chart. |With label|Without label|With group by field| |---|---|---| |![image](https://github.com/user-attachments/assets/b4f1a1e7-8e67-4b24-ae48-379fe393f90a)|![image](https://github.com/user-attachments/assets/3ef2733f-9ce8-4b37-8ecf-c19c1371aced)|![image](https://github.com/user-attachments/assets/d638f49e-98e2-4df7-8852-84dc62b9f739)| --- .../components/rule_condition_chart/rule_condition_chart.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx index 407637520dda..4567d7c37b10 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx @@ -68,6 +68,7 @@ export interface RuleConditionChartExpressions { timeSize?: number; timeUnit?: TimeUnitChar; equation?: string; + label?: string; } export interface RuleConditionChartProps { metricExpression: RuleConditionChartExpressions; @@ -108,6 +109,7 @@ export function RuleConditionChart({ threshold, comparator, equation, + label, warningComparator, warningThreshold, } = metricExpression; @@ -332,7 +334,7 @@ export function RuleConditionChart({ const baseLayer = { type: 'formula', value: formula, - label: formula, + label: label ?? formula, groupBy, format: { id: formatId, @@ -409,6 +411,7 @@ export function RuleConditionChart({ comparator, dataView, equation, + label, searchConfiguration, formula, formulaAsync.value, From 9bb3661060e01628a052e34bd471ecea0b428fa7 Mon Sep 17 00:00:00 2001 From: Elena Shostak <165678770+elena-shostak@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:05:56 +0100 Subject: [PATCH 052/100] Changed log level for message with authz opt out (#199678) ## Summary Changed log level for message with authz opt out from `warn` to `debug` __Closes: https://github.com/elastic/kibana/issues/199677__ --- .../security/server/authorization/api_authorization.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index 2b99c2a176ac..888d74e7d7bb 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -48,10 +48,6 @@ export function initAPIAuthorization( if (security) { if (isAuthzDisabled(security.authz)) { - logger.warn( - `Route authz is disabled for ${request.url.pathname}${request.url.search}": ${security.authz.reason}` - ); - return toolkit.next(); } From 763b5deafd0eec37e960e499299f1205e6a3e999 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 12 Nov 2024 23:21:58 +1100 Subject: [PATCH 053/100] Unauthorized route migration for routes owned by kibana-core (#198333) ### Authz API migration for unauthorized routes This PR migrates unauthorized routes owned by your team to a new security configuration. Please refer to the documentation for more information: [Authorization API](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization) ### **Before migration:** ```ts router.get({ path: '/api/path', ... }, handler); ``` ### **After migration:** ```ts router.get({ path: '/api/path', security: { authz: { enabled: false, reason: 'This route is opted out from authorization because ...', }, }, ... }, handler); ``` ### What to do next? 1. Review the changes in this PR. 2. Elaborate on the reasoning to opt-out of authorization. 3. Routes without a compelling reason to opt-out of authorization should plan to introduce them as soon as possible. 2. You might need to update your tests to reflect the new security configuration: - If you have snapshot tests that include the route definition. ## Any questions? If you have any questions or need help with API authorization, please reach out to the `@elastic/kibana-security` team. Co-authored-by: Jean-Louis Leysens --- .../server/routes/fetch_es_hits_status.ts | 6 ++ .../server/routes/bulk_delete.ts | 6 ++ .../server/routes/bulk_get.ts | 6 ++ .../server/routes/find.ts | 6 ++ .../server/routes/get_allowed_types.ts | 6 ++ .../server/routes/relationships.ts | 6 ++ .../server/routes/scroll_count.ts | 6 ++ .../server/routes/telemetry_config.ts | 44 +++++++++++++-- .../server/routes/telemetry_last_reported.ts | 56 +++++++++++++++++-- .../server/routes/telemetry_opt_in.ts | 28 +++++++++- .../server/routes/telemetry_opt_in_stats.ts | 6 ++ .../server/routes/telemetry_usage_stats.ts | 28 +++++++++- .../routes/telemetry_user_has_seen_notice.ts | 28 +++++++++- .../server/routes/stats/stats.ts | 6 ++ .../server/routes/ui_counters.ts | 6 ++ .../server/routes/elasticsearch_routes.ts | 28 +++++++--- .../licensing/server/routes/feature_usage.ts | 11 +++- .../plugins/licensing/server/routes/info.ts | 11 +++- .../routes/internal/notify_feature_usage.ts | 6 ++ .../routes/internal/register_feature.ts | 6 ++ 20 files changed, 281 insertions(+), 25 deletions(-) diff --git a/src/plugins/home/server/routes/fetch_es_hits_status.ts b/src/plugins/home/server/routes/fetch_es_hits_status.ts index f01660876210..aa4c1286ce67 100644 --- a/src/plugins/home/server/routes/fetch_es_hits_status.ts +++ b/src/plugins/home/server/routes/fetch_es_hits_status.ts @@ -14,6 +14,12 @@ export const registerHitsStatusRoute = (router: IRouter) => { router.post( { path: '/api/home/hits_status', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.object({ index: schema.string(), diff --git a/src/plugins/saved_objects_management/server/routes/bulk_delete.ts b/src/plugins/saved_objects_management/server/routes/bulk_delete.ts index ed82726ade0d..da082cc85a1b 100644 --- a/src/plugins/saved_objects_management/server/routes/bulk_delete.ts +++ b/src/plugins/saved_objects_management/server/routes/bulk_delete.ts @@ -15,6 +15,12 @@ export const registerBulkDeleteRoute = (router: IRouter) => { router.post( { path: '/internal/kibana/management/saved_objects/_bulk_delete', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.arrayOf( schema.object({ diff --git a/src/plugins/saved_objects_management/server/routes/bulk_get.ts b/src/plugins/saved_objects_management/server/routes/bulk_get.ts index 9b7c1505cc69..59efd552196c 100644 --- a/src/plugins/saved_objects_management/server/routes/bulk_get.ts +++ b/src/plugins/saved_objects_management/server/routes/bulk_get.ts @@ -20,6 +20,12 @@ export const registerBulkGetRoute = ( router.post( { path: '/api/kibana/management/saved_objects/_bulk_get', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.arrayOf( schema.object({ diff --git a/src/plugins/saved_objects_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts index 8b37c3550f7d..e1c028a4fab2 100644 --- a/src/plugins/saved_objects_management/server/routes/find.ts +++ b/src/plugins/saved_objects_management/server/routes/find.ts @@ -34,6 +34,12 @@ export const registerFindRoute = ( router.get( { path: '/api/kibana/management/saved_objects/_find', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { query: schema.object({ perPage: schema.number({ min: 0, defaultValue: 20 }), diff --git a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts index c484f5643d8c..6b89643e8142 100644 --- a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts +++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts @@ -23,6 +23,12 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => { router.get( { path: '/api/kibana/management/saved_objects/_allowed_types', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: false, }, async (context, req, res) => { diff --git a/src/plugins/saved_objects_management/server/routes/relationships.ts b/src/plugins/saved_objects_management/server/routes/relationships.ts index 7f06e6a5dabc..d39f001f2f2a 100644 --- a/src/plugins/saved_objects_management/server/routes/relationships.ts +++ b/src/plugins/saved_objects_management/server/routes/relationships.ts @@ -21,6 +21,12 @@ export const registerRelationshipsRoute = ( router.get( { path: '/api/kibana/management/saved_objects/relationships/{type}/{id}', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { params: schema.object({ type: schema.string(), diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts index 31c01536d600..3116ca3a22fb 100644 --- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts +++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts @@ -17,6 +17,12 @@ export const registerScrollForCountRoute = (router: IRouter) => { router.post( { path: '/api/kibana/management/saved_objects/scroll/counts', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.object({ typesToInclude: schema.arrayOf(schema.string()), diff --git a/src/plugins/telemetry/server/routes/telemetry_config.ts b/src/plugins/telemetry/server/routes/telemetry_config.ts index ebc5ca53985e..700e778ceea2 100644 --- a/src/plugins/telemetry/server/routes/telemetry_config.ts +++ b/src/plugins/telemetry/server/routes/telemetry_config.ts @@ -102,12 +102,46 @@ export function registerTelemetryConfigRoutes({ options: { authRequired: 'optional' }, }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: v2Validations }, v2Handler) - .addVersion({ version: '2', validate: v2Validations }, v2Handler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ); // Register the deprecated public and path-based for BWC // as we know this one is used by other Elastic products to fetch the opt-in status. - router.versioned - .get({ access: 'public', path: FetchTelemetryConfigRoutePathBasedV2 }) - .addVersion({ version: '2023-10-31', validate: v2Validations }, v2Handler); + router.versioned.get({ access: 'public', path: FetchTelemetryConfigRoutePathBasedV2 }).addVersion( + { + version: '2023-10-31', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ); } diff --git a/src/plugins/telemetry/server/routes/telemetry_last_reported.ts b/src/plugins/telemetry/server/routes/telemetry_last_reported.ts index 299d457e080b..932b7ab8db9c 100644 --- a/src/plugins/telemetry/server/routes/telemetry_last_reported.ts +++ b/src/plugins/telemetry/server/routes/telemetry_last_reported.ts @@ -40,8 +40,32 @@ export function registerTelemetryLastReported( router.versioned .get({ access: 'internal', path: LastReportedRoute }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: v2GetValidations }, v2GetHandler) - .addVersion({ version: '2', validate: v2GetValidations }, v2GetHandler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2GetValidations, + }, + v2GetHandler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2GetValidations, + }, + v2GetHandler + ); // PUT to update const v2PutHandler: RequestHandler = async (context, req, res) => { @@ -55,6 +79,30 @@ export function registerTelemetryLastReported( router.versioned .put({ access: 'internal', path: LastReportedRoute }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: false }, v2PutHandler) - .addVersion({ version: '2', validate: false }, v2PutHandler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, + v2PutHandler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, + v2PutHandler + ); } diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts index 21b674c3dc45..de13162ff061 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts @@ -128,6 +128,30 @@ export function registerTelemetryOptInRoutes({ router.versioned .post({ access: 'internal', path: OptInRoute }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: v2Validations }, v2Handler) - .addVersion({ version: '2', validate: v2Validations }, v2Handler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ); } diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts index dcd7790b67e6..0c38b5981850 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts @@ -72,6 +72,12 @@ export function registerTelemetryOptInStatsRoutes( .addVersion( { version: '2023-10-31', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { request: { body: schema.object({ diff --git a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts index f19ec804ac6e..28198c853e4b 100644 --- a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts @@ -97,6 +97,30 @@ export function registerTelemetryUsageStatsRoutes( enableQueryVersion: true, // Allow specifying the version through querystring so that we can use it in Dev Console }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: v2Validations }, v2Handler) - .addVersion({ version: '2', validate: v2Validations }, v2Handler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: v2Validations, + }, + v2Handler + ); } diff --git a/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts b/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts index 54eaae80648e..dff8a8f2d5bd 100644 --- a/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts +++ b/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts @@ -56,6 +56,30 @@ export function registerTelemetryUserHasSeenNotice(router: IRouter, currentKiban router.versioned .put({ access: 'internal', path: UserHasSeenNoticeRoute }) // Just because it used to be /v2/, we are creating identical v1 and v2. - .addVersion({ version: '1', validate: false }, v2Handler) - .addVersion({ version: '2', validate: false }, v2Handler); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, + v2Handler + ) + .addVersion( + { + version: '2', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, + v2Handler + ); } diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 95a82abbbf15..3a7683bb1c46 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -54,6 +54,12 @@ export function registerStatsRoute({ router.get( { path: '/api/stats', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, options: { authRequired: !config.allowAnonymous, // The `api` tag ensures that unauthenticated calls receive a 401 rather than a 302 redirect to login page. diff --git a/src/plugins/usage_collection/server/routes/ui_counters.ts b/src/plugins/usage_collection/server/routes/ui_counters.ts index 2545afb7f90f..fbeba04ac190 100644 --- a/src/plugins/usage_collection/server/routes/ui_counters.ts +++ b/src/plugins/usage_collection/server/routes/ui_counters.ts @@ -21,6 +21,12 @@ export function registerUiCountersRoute( router.post( { path: '/api/ui_counters/_report', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.object({ report: reportSchema, diff --git a/x-pack/plugins/cloud/server/routes/elasticsearch_routes.ts b/x-pack/plugins/cloud/server/routes/elasticsearch_routes.ts index 5cdc2f90559c..d3a5c4bebf30 100644 --- a/x-pack/plugins/cloud/server/routes/elasticsearch_routes.ts +++ b/x-pack/plugins/cloud/server/routes/elasticsearch_routes.ts @@ -24,12 +24,24 @@ export function defineRoutes({ path: ELASTICSEARCH_CONFIG_ROUTE, access: 'internal', }) - .addVersion({ version: '1', validate: {} }, async (context, request, response) => { - const body: ElasticsearchConfigType = { - elasticsearch_url: elasticsearchUrl, - }; - return response.ok({ - body, - }); - }); + .addVersion( + { + version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: {}, + }, + async (context, request, response) => { + const body: ElasticsearchConfigType = { + elasticsearch_url: elasticsearchUrl, + }; + return response.ok({ + body, + }); + } + ); } diff --git a/x-pack/plugins/licensing/server/routes/feature_usage.ts b/x-pack/plugins/licensing/server/routes/feature_usage.ts index 33317ab09cb9..acb3303834de 100644 --- a/x-pack/plugins/licensing/server/routes/feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/feature_usage.ts @@ -14,7 +14,16 @@ export function registerFeatureUsageRoute( getStartServices: StartServicesAccessor<{}, LicensingPluginStart> ) { router.get( - { path: '/api/licensing/feature_usage', validate: false }, + { + path: '/api/licensing/feature_usage', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, async (context, request, response) => { const [, , { featureUsage }] = await getStartServices(); return response.ok({ diff --git a/x-pack/plugins/licensing/server/routes/info.ts b/x-pack/plugins/licensing/server/routes/info.ts index 1cbc7b639896..c73508fac823 100644 --- a/x-pack/plugins/licensing/server/routes/info.ts +++ b/x-pack/plugins/licensing/server/routes/info.ts @@ -9,7 +9,16 @@ import { LicensingRouter } from '../types'; export function registerInfoRoute(router: LicensingRouter) { router.get( - { path: '/api/licensing/info', validate: false }, + { + path: '/api/licensing/info', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, + validate: false, + }, async (context, request, response) => { return response.ok({ body: (await context.licensing).license, diff --git a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts index a33cf4284245..c3c70243005e 100644 --- a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts @@ -12,6 +12,12 @@ export function registerNotifyFeatureUsageRoute(router: LicensingRouter) { router.post( { path: '/internal/licensing/feature_usage/notify', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.object({ featureName: schema.string(), diff --git a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts index 0c043c7c127a..6b35c32a266d 100644 --- a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts +++ b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts @@ -17,6 +17,12 @@ export function registerRegisterFeatureRoute( router.post( { path: '/internal/licensing/feature_usage/register', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { body: schema.arrayOf( schema.object({ From 73694426f943f00f74556406dcb26c66eb4a772a Mon Sep 17 00:00:00 2001 From: mohamedhamed-ahmed Date: Tue, 12 Nov 2024 12:26:36 +0000 Subject: [PATCH 054/100] [Discover] Breakdown support for fieldstats (#199028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/elastic/kibana/issues/192700 ## 📝 Summary This PR add a new `Add breakdown` button to the field stats popover for all applicable fields. ## 🎥 Demo https://github.com/user-attachments/assets/d647189c-9b04-4127-a4fd-f9764babe46e --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-esql-utils/index.ts | 1 + packages/kbn-esql-utils/src/index.ts | 6 ++- .../src/utils/esql_fields_utils.test.ts | 49 ++++++++++++++++++- .../src/utils/esql_fields_utils.ts | 29 +++++++---- packages/kbn-field-utils/index.ts | 1 + .../utils/field_supports_breakdown.test.ts | 0 .../src}/utils/field_supports_breakdown.ts | 12 +++-- .../field_popover_header.test.tsx | 28 ++++++++++- .../field_popover/field_popover_header.tsx | 24 +++++++++ .../field_list_item.test.tsx | 36 ++++++++++++++ .../field_list_item.tsx | 17 +++++-- .../field_list_sidebar.tsx | 36 ++++++++------ packages/kbn-unified-field-list/tsconfig.json | 3 +- .../components/layout/discover_layout.tsx | 32 +++++++++--- .../discover_sidebar_responsive.test.tsx | 14 ++++++ .../sidebar/discover_sidebar_responsive.tsx | 28 ++++++----- .../public/chart/breakdown_field_selector.tsx | 8 ++- src/plugins/unified_histogram/public/index.ts | 1 - .../public/services/lens_vis_service.ts | 2 +- .../apps/discover/esql/_esql_view.ts | 17 +++++++ .../group1/_discover_histogram_breakdown.ts | 10 +++- .../page_objects/unified_field_list.ts | 10 ++++ .../public/hooks/use_degraded_docs_chart.ts | 2 +- .../dataset_quality/tsconfig.json | 3 +- 24 files changed, 307 insertions(+), 62 deletions(-) rename {src/plugins/unified_histogram/public => packages/kbn-field-utils/src}/utils/field_supports_breakdown.test.ts (100%) rename {src/plugins/unified_histogram/public => packages/kbn-field-utils/src}/utils/field_supports_breakdown.ts (65%) diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index ee47c0321e2e..9519e84624cb 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -31,6 +31,7 @@ export { getQueryColumnsFromESQLQuery, isESQLColumnSortable, isESQLColumnGroupable, + isESQLFieldGroupable, TextBasedLanguages, } from './src'; diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index cf530be20d7a..6cf2dd031bf0 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -31,4 +31,8 @@ export { getStartEndParams, hasStartEndParams, } from './utils/run_query'; -export { isESQLColumnSortable, isESQLColumnGroupable } from './utils/esql_fields_utils'; +export { + isESQLColumnSortable, + isESQLColumnGroupable, + isESQLFieldGroupable, +} from './utils/esql_fields_utils'; diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts index bfc3a4d6708f..5bf9980d4ae7 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts @@ -7,7 +7,12 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { isESQLColumnSortable, isESQLColumnGroupable } from './esql_fields_utils'; +import { + isESQLColumnSortable, + isESQLColumnGroupable, + isESQLFieldGroupable, +} from './esql_fields_utils'; +import type { FieldSpec } from '@kbn/data-views-plugin/common'; describe('esql fields helpers', () => { describe('isESQLColumnSortable', () => { @@ -104,4 +109,46 @@ describe('esql fields helpers', () => { expect(isESQLColumnGroupable(keywordField)).toBeTruthy(); }); }); + + describe('isESQLFieldGroupable', () => { + it('returns false for unsupported fields', () => { + const fieldSpec: FieldSpec = { + name: 'unsupported', + type: 'unknown', + esTypes: ['unknown'], + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeFalsy(); + }); + + it('returns false for counter fields', () => { + const fieldSpec: FieldSpec = { + name: 'tsbd_counter', + type: 'number', + esTypes: ['long'], + timeSeriesMetric: 'counter', + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeFalsy(); + }); + + it('returns true for everything else', () => { + const fieldSpec: FieldSpec = { + name: 'sortable', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: false, + isNull: false, + }; + + expect(isESQLFieldGroupable(fieldSpec)).toBeTruthy(); + }); + }); }); diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts index e2c5c785f8f5..fa0ae534197f 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import type { FieldSpec } from '@kbn/data-views-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; const SPATIAL_FIELDS = ['geo_point', 'geo_shape', 'point', 'shape']; @@ -40,22 +41,30 @@ export const isESQLColumnSortable = (column: DatatableColumn): boolean => { return true; }; +// Helper function to check if a field is groupable based on its type and esType +const isGroupable = (type: string | undefined, esType: string | undefined): boolean => { + // we don't allow grouping on the unknown field types + if (type === UNKNOWN_FIELD) { + return false; + } + // we don't allow grouping on tsdb counter fields + if (esType && esType.indexOf(TSDB_COUNTER_FIELDS_PREFIX) !== -1) { + return false; + } + return true; +}; + /** * Check if a column is groupable (| STATS ... BY ). * * @param column The DatatableColumn of the field. * @returns True if the column is groupable, false otherwise. */ - export const isESQLColumnGroupable = (column: DatatableColumn): boolean => { - // we don't allow grouping on the unknown field types - if (column.meta?.type === UNKNOWN_FIELD) { - return false; - } - // we don't allow grouping on tsdb counter fields - if (column.meta?.esType && column.meta?.esType?.indexOf(TSDB_COUNTER_FIELDS_PREFIX) !== -1) { - return false; - } + return isGroupable(column.meta?.type, column.meta?.esType); +}; - return true; +export const isESQLFieldGroupable = (field: FieldSpec): boolean => { + if (field.timeSeriesMetric === 'counter') return false; + return isGroupable(field.type, field.esTypes?.[0]); }; diff --git a/packages/kbn-field-utils/index.ts b/packages/kbn-field-utils/index.ts index ae77f0d1524f..d3ce774177dc 100644 --- a/packages/kbn-field-utils/index.ts +++ b/packages/kbn-field-utils/index.ts @@ -25,6 +25,7 @@ export { comboBoxFieldOptionMatcher, getFieldSearchMatchingHighlight, } from './src/utils/field_name_wildcard_matcher'; +export { fieldSupportsBreakdown } from './src/utils/field_supports_breakdown'; export { FieldIcon, type FieldIconProps, getFieldIconProps } from './src/components/field_icon'; export { FieldDescription, type FieldDescriptionProps } from './src/components/field_description'; diff --git a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.test.ts b/packages/kbn-field-utils/src/utils/field_supports_breakdown.test.ts similarity index 100% rename from src/plugins/unified_histogram/public/utils/field_supports_breakdown.test.ts rename to packages/kbn-field-utils/src/utils/field_supports_breakdown.test.ts diff --git a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts b/packages/kbn-field-utils/src/utils/field_supports_breakdown.ts similarity index 65% rename from src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts rename to packages/kbn-field-utils/src/utils/field_supports_breakdown.ts index 12fdfbf20aa3..3177d40012b5 100644 --- a/src/plugins/unified_histogram/public/utils/field_supports_breakdown.ts +++ b/packages/kbn-field-utils/src/utils/field_supports_breakdown.ts @@ -7,12 +7,18 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DataViewField } from '@kbn/data-views-plugin/public'; +import { type DataViewField } from '@kbn/data-views-plugin/common'; +import { KNOWN_FIELD_TYPES } from './field_types'; -const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); +const supportedTypes = new Set([ + KNOWN_FIELD_TYPES.STRING, + KNOWN_FIELD_TYPES.BOOLEAN, + KNOWN_FIELD_TYPES.NUMBER, + KNOWN_FIELD_TYPES.IP, +]); export const fieldSupportsBreakdown = (field: DataViewField) => - supportedTypes.has(field.type) && + supportedTypes.has(field.type as KNOWN_FIELD_TYPES) && field.aggregatable && !field.scripted && field.timeSeriesMetric !== 'counter'; diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx index 19fee33bc6ea..a4e322f2b681 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.test.tsx @@ -38,6 +38,7 @@ describe('UnifiedFieldList ', () => { field={field} closePopover={mockClose} onAddFieldToWorkspace={jest.fn()} + onAddBreakdownField={jest.fn()} onAddFilter={jest.fn()} onEditField={jest.fn()} onDeleteField={jest.fn()} @@ -45,6 +46,9 @@ describe('UnifiedFieldList ', () => { ); expect(wrapper.text()).toBe(fieldName); + expect( + wrapper.find(`[data-test-subj="fieldPopoverHeader_addBreakdownField-${fieldName}"]`).exists() + ).toBeTruthy(); expect( wrapper.find(`[data-test-subj="fieldPopoverHeader_addField-${fieldName}"]`).exists() ).toBeTruthy(); @@ -57,7 +61,29 @@ describe('UnifiedFieldList ', () => { expect( wrapper.find(`[data-test-subj="fieldPopoverHeader_deleteField-${fieldName}"]`).exists() ).toBeTruthy(); - expect(wrapper.find(EuiButtonIcon)).toHaveLength(4); + expect(wrapper.find(EuiButtonIcon)).toHaveLength(5); + }); + + it('should correctly handle add-breakdown-field action', async () => { + const mockClose = jest.fn(); + const mockAddBreakdownField = jest.fn(); + const fieldName = 'extension'; + const field = dataView.fields.find((f) => f.name === fieldName)!; + const wrapper = mountWithIntl( + + ); + + wrapper + .find(`[data-test-subj="fieldPopoverHeader_addBreakdownField-${fieldName}"]`) + .first() + .simulate('click'); + + expect(mockClose).toHaveBeenCalled(); + expect(mockAddBreakdownField).toHaveBeenCalledWith(field); }); it('should correctly handle add-field action', async () => { diff --git a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx index 65d5a1da9803..3babd0587191 100644 --- a/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx +++ b/packages/kbn-unified-field-list/src/components/field_popover/field_popover_header.tsx @@ -31,6 +31,7 @@ export interface FieldPopoverHeaderProps { buttonAddFilterProps?: Partial; buttonEditFieldProps?: Partial; buttonDeleteFieldProps?: Partial; + onAddBreakdownField?: (field: DataViewField | undefined) => void; onAddFieldToWorkspace?: (field: DataViewField) => unknown; onAddFilter?: AddFieldFilterHandler; onEditField?: (fieldName: string) => unknown; @@ -47,6 +48,7 @@ export const FieldPopoverHeader: React.FC = ({ buttonAddFilterProps, buttonEditFieldProps, buttonDeleteFieldProps, + onAddBreakdownField, onAddFieldToWorkspace, onAddFilter, onEditField, @@ -82,6 +84,13 @@ export const FieldPopoverHeader: React.FC = ({ defaultMessage: 'Delete data view field', }); + const addBreakdownFieldTooltip = i18n.translate( + 'unifiedFieldList.fieldPopover.addBreakdownFieldLabel', + { + defaultMessage: 'Add breakdown', + } + ); + return ( <> @@ -108,6 +117,21 @@ export const FieldPopoverHeader: React.FC = ({ )} + {onAddBreakdownField && ( + + + { + closePopover(); + onAddBreakdownField(field); + }} + /> + + + )} {onAddFilter && field.filterable && !field.scripted && ( diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx index a5b1955aeca2..671a4175ad10 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.test.tsx @@ -43,10 +43,12 @@ async function getComponent({ selected = false, field, canFilter = true, + isBreakdownSupported = true, }: { selected?: boolean; field?: DataViewField; canFilter?: boolean; + isBreakdownSupported?: boolean; }) { const finalField = field ?? @@ -76,6 +78,7 @@ async function getComponent({ dataView: stubDataView, field: finalField, ...(canFilter && { onAddFilter: jest.fn() }), + ...(isBreakdownSupported && { onAddBreakdownField: jest.fn() }), onAddFieldToWorkspace: jest.fn(), onRemoveFieldFromWorkspace: jest.fn(), onEditField: jest.fn(), @@ -137,6 +140,34 @@ describe('UnifiedFieldListItem', function () { expect(comp.find(FieldItemButton).prop('onClick')).toBeUndefined(); }); + + it('should not show addBreakdownField action button if not supported', async function () { + const field = new DataViewField({ + name: 'extension.keyword', + type: 'string', + esTypes: ['keyword'], + aggregatable: true, + searchable: true, + }); + const { comp } = await getComponent({ + field, + isBreakdownSupported: false, + }); + + await act(async () => { + const fieldItem = findTestSubject(comp, 'field-extension.keyword-showDetails'); + await fieldItem.simulate('click'); + await comp.update(); + }); + + await comp.update(); + + expect( + comp + .find('[data-test-subj="fieldPopoverHeader_addBreakdownField-extension.keyword"]') + .exists() + ).toBeFalsy(); + }); it('should request field stats', async function () { const field = new DataViewField({ name: 'machine.os.raw', @@ -189,6 +220,11 @@ describe('UnifiedFieldListItem', function () { await comp.update(); expect(comp.find(EuiPopover).prop('isOpen')).toBe(true); + expect( + comp + .find('[data-test-subj="fieldPopoverHeader_addBreakdownField-extension.keyword"]') + .exists() + ).toBeTruthy(); expect( comp.find('[data-test-subj="fieldPopoverHeader_addField-extension.keyword"]').exists() ).toBeTruthy(); diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx index b139e7b5685c..2ff7d22de39d 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_item/field_list_item.tsx @@ -15,6 +15,8 @@ import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/publ import { Draggable } from '@kbn/dom-drag-drop'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { Filter } from '@kbn/es-query'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; +import { isESQLFieldGroupable } from '@kbn/esql-utils'; import type { SearchMode } from '../../types'; import { FieldItemButton, type FieldItemButtonProps } from '../../components/field_item_button'; import { @@ -140,6 +142,10 @@ export interface UnifiedFieldListItemProps { * The currently selected data view */ dataView: DataView; + /** + * Callback to update breakdown field + */ + onAddBreakdownField?: (breakdownField: DataViewField | undefined) => void; /** * Callback to add/select the field */ @@ -215,6 +221,7 @@ function UnifiedFieldListItemComponent({ field, highlight, dataView, + onAddBreakdownField, onAddFieldToWorkspace, onRemoveFieldFromWorkspace, onAddFilter, @@ -232,6 +239,9 @@ function UnifiedFieldListItemComponent({ }: UnifiedFieldListItemProps) { const [infoIsOpen, setOpen] = useState(false); + const isBreakdownSupported = + searchMode === 'documents' ? fieldSupportsBreakdown(field) : isESQLFieldGroupable(field); + const addFilterAndClosePopover: typeof onAddFilter | undefined = useMemo( () => onAddFilter @@ -394,13 +404,14 @@ function UnifiedFieldListItemComponent({ data-test-subj={stateService.creationOptions.dataTestSubj?.fieldListItemPopoverDataTestSubj} renderHeader={() => ( )} diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx index a7d8fb4616cb..2d23903c34ea 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar.tsx @@ -48,6 +48,7 @@ export type UnifiedFieldListSidebarCustomizableProps = Pick< | 'dataView' | 'trackUiMetric' | 'onAddFilter' + | 'onAddBreakdownField' | 'onAddFieldToWorkspace' | 'onRemoveFieldFromWorkspace' | 'additionalFilters' @@ -161,6 +162,7 @@ export const UnifiedFieldListSidebarComponent: React.FC (
  • ), @@ -298,6 +301,7 @@ export const UnifiedFieldListSidebarComponent: React.FC + isOfAggregateQueryType(query) + ? dataView?.isTimeBased() && !hasTransformationalCommand(query.esql) + : true, + [dataView, query] + ); + + const onAddBreakdownField = useCallback( + (field: DataViewField | undefined) => { + stateContainer.appState.update({ breakdownField: field?.name }); + }, + [stateContainer] + ); + const onFieldEdited = useCallback( async ({ removedFieldName }: { removedFieldName?: string } = {}) => { if (removedFieldName && currentColumns.includes(removedFieldName)) { @@ -423,18 +438,19 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { sidebarToggleState$={sidebarToggleState$} sidebarPanel={ } mainPanel={ diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index 6147bb916dad..296d403da690 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -162,6 +162,7 @@ function getCompProps(options?: { hits?: DataTableRecord[] }): DiscoverSidebarRe result: hits, }) as DataDocuments$, onChangeDataView: jest.fn(), + onAddBreakdownField: jest.fn(), onAddFilter: jest.fn(), onAddField: jest.fn(), onRemoveField: jest.fn(), @@ -397,6 +398,19 @@ describe('discover responsive sidebar', function () { expect(ExistingFieldsServiceApi.loadFieldExisting).not.toHaveBeenCalled(); }); + it('should allow adding breakdown field', async function () { + const comp = await mountComponent(props); + const availableFields = findTestSubject(comp, 'fieldListGroupedAvailableFields'); + await act(async () => { + const button = findTestSubject(availableFields, 'field-extension-showDetails'); + button.simulate('click'); + comp.update(); + }); + + comp.update(); + findTestSubject(comp, 'fieldPopoverHeader_addBreakdownField-extension').simulate('click'); + expect(props.onAddBreakdownField).toHaveBeenCalled(); + }); it('should allow selecting fields', async function () { const comp = await mountComponent(props); const availableFields = findTestSubject(comp, 'fieldListGroupedAvailableFields'); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 80a3b9d412c7..17a3e9def8b2 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -87,6 +87,10 @@ export interface DiscoverSidebarResponsiveProps { * hits fetched from ES, displayed in the doc table */ documents$: DataDocuments$; + /** + * Callback to update breakdown field + */ + onAddBreakdownField?: (breakdownField: DataViewField | undefined) => void; /** * Callback function when selecting a field */ @@ -151,6 +155,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) selectedDataView, columns, trackUiMetric, + onAddBreakdownField, onAddFilter, onFieldEdited, onDataViewCreated, @@ -373,23 +378,24 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) {selectedDataView ? ( ) : null} diff --git a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx index 0d6aa1e75fe8..418892bd5434 100644 --- a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx +++ b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx @@ -9,7 +9,12 @@ import React, { useCallback, useMemo } from 'react'; import { EuiSelectableOption } from '@elastic/eui'; -import { FieldIcon, getFieldIconProps, comboBoxFieldOptionMatcher } from '@kbn/field-utils'; +import { + FieldIcon, + getFieldIconProps, + comboBoxFieldOptionMatcher, + fieldSupportsBreakdown, +} from '@kbn/field-utils'; import { css } from '@emotion/react'; import { isESQLColumnGroupable } from '@kbn/esql-utils'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/common'; @@ -17,7 +22,6 @@ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import { i18n } from '@kbn/i18n'; import { UnifiedHistogramBreakdownContext } from '../types'; -import { fieldSupportsBreakdown } from '../utils/field_supports_breakdown'; import { ToolbarSelector, ToolbarSelectorProps, diff --git a/src/plugins/unified_histogram/public/index.ts b/src/plugins/unified_histogram/public/index.ts index fda59c78819d..f79db6ce8a51 100644 --- a/src/plugins/unified_histogram/public/index.ts +++ b/src/plugins/unified_histogram/public/index.ts @@ -36,6 +36,5 @@ export type { } from './types'; export { UnifiedHistogramFetchStatus, UnifiedHistogramExternalVisContextStatus } from './types'; export { canImportVisContext } from './utils/external_vis_context'; -export { fieldSupportsBreakdown } from './utils/field_supports_breakdown'; export const plugin = () => new UnifiedHistogramPublicPlugin(); diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.ts index 2367e729b5a7..04bf810848f2 100644 --- a/src/plugins/unified_histogram/public/services/lens_vis_service.ts +++ b/src/plugins/unified_histogram/public/services/lens_vis_service.ts @@ -36,6 +36,7 @@ import { LegendSize } from '@kbn/visualizations-plugin/public'; import { XYConfiguration } from '@kbn/visualizations-plugin/common'; import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; import { UnifiedHistogramExternalVisContextStatus, UnifiedHistogramSuggestionContext, @@ -49,7 +50,6 @@ import { injectESQLQueryIntoLensLayers, } from '../utils/external_vis_context'; import { computeInterval } from '../utils/compute_interval'; -import { fieldSupportsBreakdown } from '../utils/field_supports_breakdown'; import { shouldDisplayHistogram } from '../layout/helpers'; import { enrichLensAttributesWithTablesData } from '../utils/lens_vis_from_table'; diff --git a/test/functional/apps/discover/esql/_esql_view.ts b/test/functional/apps/discover/esql/_esql_view.ts index 272de320d205..18841d07dac3 100644 --- a/test/functional/apps/discover/esql/_esql_view.ts +++ b/test/functional/apps/discover/esql/_esql_view.ts @@ -843,6 +843,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const list = await discover.getHistogramLegendList(); expect(list).to.eql(['css', 'gif', 'jpg', 'php', 'png']); }); + + it('should choose breakdown field when selected from field stats', async () => { + await discover.selectTextBaseLang(); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + const testQuery = 'from logstash-*'; + await monacoEditor.setCodeEditorValue(testQuery); + await testSubjects.click('querySubmitButton'); + await header.waitUntilLoadingHasFinished(); + await discover.waitUntilSearchingHasFinished(); + + await unifiedFieldList.clickFieldListAddBreakdownField('extension'); + await header.waitUntilLoadingHasFinished(); + const list = await discover.getHistogramLegendList(); + expect(list).to.eql(['css', 'gif', 'jpg', 'php', 'png']); + }); }); }); } diff --git a/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts b/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts index 5ce8732e5355..6f04a6df1b6b 100644 --- a/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts +++ b/test/functional/apps/discover/group1/_discover_histogram_breakdown.ts @@ -14,11 +14,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const filterBar = getService('filterBar'); - const { common, discover, header, timePicker } = getPageObjects([ + const { common, discover, header, timePicker, unifiedFieldList } = getPageObjects([ 'common', 'discover', 'header', 'timePicker', + 'unifiedFieldList', ]); describe('discover unified histogram breakdown', function describeIndexTests() { @@ -36,6 +37,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); }); + it('should apply breakdown when selected from field stats', async () => { + await unifiedFieldList.clickFieldListAddBreakdownField('geo.dest'); + await header.waitUntilLoadingHasFinished(); + const list = await discover.getHistogramLegendList(); + expect(list).to.eql(['CN', 'IN', 'US', 'Other']); + }); + it('should choose breakdown field', async () => { await discover.chooseBreakdownField('extension.raw'); await header.waitUntilLoadingHasFinished(); diff --git a/test/functional/page_objects/unified_field_list.ts b/test/functional/page_objects/unified_field_list.ts index 14006c8c5d62..4751769f717b 100644 --- a/test/functional/page_objects/unified_field_list.ts +++ b/test/functional/page_objects/unified_field_list.ts @@ -226,6 +226,16 @@ export class UnifiedFieldListPageObject extends FtrService { await this.testSubjects.missingOrFail(`fieldVisualize-${field}`); } + public async clickFieldListAddBreakdownField(field: string) { + const addBreakdownFieldTestSubj = `fieldPopoverHeader_addBreakdownField-${field}`; + if (!(await this.testSubjects.exists(addBreakdownFieldTestSubj))) { + // field has to be open + await this.clickFieldListItem(field); + } + await this.testSubjects.click(addBreakdownFieldTestSubj); + await this.header.waitUntilLoadingHasFinished(); + } + public async clickFieldListPlusFilter(field: string, value: string) { const plusFilterTestSubj = `plus-${field}-${value}`; if (!(await this.testSubjects.exists(plusFilterTestSubj))) { diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts index d3fad141335d..90779613f0c0 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_degraded_docs_chart.ts @@ -7,10 +7,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { Action } from '@kbn/ui-actions-plugin/public'; -import { fieldSupportsBreakdown } from '@kbn/unified-histogram-plugin/public'; import { i18n } from '@kbn/i18n'; import { useEuiTheme } from '@elastic/eui'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { fieldSupportsBreakdown } from '@kbn/field-utils'; import { DEFAULT_LOGS_DATA_VIEW } from '../../common/constants'; import { useCreateDataView } from './use_create_dataview'; import { useKibanaContextForPlugin } from '../utils'; diff --git a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json index f0d82fadb54a..8576b08bd947 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json +++ b/x-pack/plugins/observability_solution/dataset_quality/tsconfig.json @@ -60,7 +60,8 @@ "@kbn/usage-collection-plugin", "@kbn/rison", "@kbn/task-manager-plugin", - "@kbn/core-application-browser" + "@kbn/core-application-browser", + "@kbn/field-utils" ], "exclude": [ "target/**/*" From 627692c4ef0c19d27f67c416ec206c374722fbac Mon Sep 17 00:00:00 2001 From: Kate Sosedova <36230123+ek-so@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:32:51 +0100 Subject: [PATCH 055/100] =?UTF-8?q?Remove=20a=20condition=20to=20hide=20sp?= =?UTF-8?q?ace=20from=20dropdown=20menu=20even=20if=20there's=20o=E2=80=A6?= =?UTF-8?q?=20(#199601)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Based [on the issue](https://github.com/orgs/elastic/projects/1102/views/15?pane=issue&itemId=86718686&issue=elastic%7Ckibana%7C199594), I removed a condition to hide space from dropdown menu even if there's only one (for admins and regular users) - I also changed the wording from "Your spaces" to just "Spaces" so that we're consistent in all cases, [based on this comment](https://github.com/elastic/UX/issues/113#issuecomment-2464988675). --------- Co-authored-by: Elastic Machine --- .../spaces/public/nav_control/components/spaces_menu.tsx | 2 +- .../plugins/spaces/public/nav_control/nav_control_popover.tsx | 2 +- x-pack/plugins/translations/translations/fr-FR.json | 2 +- x-pack/plugins/translations/translations/zh-CN.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index 29d360fe91f3..3a055fabd2cb 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -117,7 +117,7 @@ class SpacesMenuUI extends Component { {search || i18n.translate('xpack.spaces.navControl.spacesMenu.selectSpacesTitle', { - defaultMessage: 'Your spaces', + defaultMessage: 'Spaces', })} {list} diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index cd08ff87e1a5..31a721e4b02e 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -97,7 +97,7 @@ class NavControlPopoverUI extends Component { const isTourOpen = Boolean(activeSpace) && this.state.showTour && !this.state.showSpaceSelector; let element: React.ReactNode; - if (this.state.loading || this.state.spaces.length < 2) { + if (this.state.loading) { element = ( Date: Tue, 12 Nov 2024 12:49:51 +0000 Subject: [PATCH 056/100] [ObsUX][APM] Migrate error_rate tests to agnostic deployment tests (#199672) ### Summary Closes https://github.com/elastic/kibana/issues/198970 Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate error_rate test folder to Deployment-agnostic testing strategy. #### How to test Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` Checks - [ ] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --- .../observability/apm/error_rate/index.ts | 15 ++++++++++ .../apm}/error_rate/service_apis.spec.ts | 26 +++++++++-------- .../apm}/error_rate/service_maps.spec.ts | 28 +++++++++++-------- .../apis/observability/apm/index.ts | 1 + 4 files changed, 47 insertions(+), 23 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/error_rate/service_apis.spec.ts (93%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/error_rate/service_maps.spec.ts (89%) diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/index.ts new file mode 100644 index 000000000000..a3dd89f0ddb1 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('error_rate', () => { + loadTestFile(require.resolve('./service_apis.spec.ts')); + loadTestFile(require.resolve('./service_maps.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_apis.spec.ts similarity index 93% rename from x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_apis.spec.ts index 2ab6b1bb97a5..56dded824a32 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_apis.spec.ts @@ -5,6 +5,7 @@ * 2.0. */ import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import expect from '@kbn/expect'; import { mean, meanBy, sumBy } from 'lodash'; import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; @@ -12,12 +13,16 @@ import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +const GO_PROD_LIST_RATE = 75; +const GO_PROD_LIST_ERROR_RATE = 25; +const GO_PROD_ID_RATE = 50; +const GO_PROD_ID_ERROR_RATE = 50; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -151,13 +156,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorRateMetricValues: Awaited>; let errorTransactionValues: Awaited>; - // FLAKY: https://github.com/elastic/kibana/issues/177321 - registry.when('Services APIs', { config: 'basic', archives: [] }, () => { + describe('Services APIs', () => { describe('when data is loaded ', () => { - const GO_PROD_LIST_RATE = 75; - const GO_PROD_LIST_ERROR_RATE = 25; - const GO_PROD_ID_RATE = 50; - const GO_PROD_ID_ERROR_RATE = 50; + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { const serviceGoProdInstance = apm .service({ name: serviceName, environment: 'production', agentName: 'go' }) @@ -166,6 +168,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionNameProductList = 'GET /api/product/list'; const transactionNameProductId = 'GET /api/product/:id'; + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await apmSynthtraceEsClient.index([ timerange(start, end) .interval('1m') diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_maps.spec.ts similarity index 89% rename from x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_maps.spec.ts index aa7d635b977c..462ad8db4bdd 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_maps.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/error_rate/service_maps.spec.ts @@ -5,17 +5,22 @@ * 2.0. */ import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import expect from '@kbn/expect'; import { meanBy } from 'lodash'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +const GO_PROD_LIST_RATE = 75; +const GO_PROD_LIST_ERROR_RATE = 25; +const GO_PROD_ID_RATE = 50; +const GO_PROD_ID_ERROR_RATE = 50; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -74,13 +79,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { let errorRateMetricValues: Awaited>; let errorTransactionValues: Awaited>; - registry.when('Service Maps APIs', { config: 'trial', archives: [] }, () => { + + describe('Service Maps APIs', () => { describe('when data is loaded ', () => { - const GO_PROD_LIST_RATE = 75; - const GO_PROD_LIST_ERROR_RATE = 25; - const GO_PROD_ID_RATE = 50; - const GO_PROD_ID_ERROR_RATE = 50; - before(() => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { const serviceGoProdInstance = apm .service({ name: serviceName, environment: 'production', agentName: 'go' }) .instance('instance-a'); @@ -88,6 +91,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { const transactionNameProductList = 'GET /api/product/list'; const transactionNameProductId = 'GET /api/product/:id'; + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + return apmSynthtraceEsClient.index([ timerange(start, end) .interval('1m') @@ -140,7 +145,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => apmSynthtraceEsClient.clean()); - // FLAKY: https://github.com/elastic/kibana/issues/172772 describe('compare latency value between service inventory and service maps', () => { before(async () => { [errorTransactionValues, errorRateMetricValues] = await Promise.all([ diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index cc56d0f2e668..a62386b536ea 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -16,6 +16,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./mobile')); loadTestFile(require.resolve('./custom_dashboards')); loadTestFile(require.resolve('./dependencies')); + loadTestFile(require.resolve('./error_rate')); loadTestFile(require.resolve('./data_view')); loadTestFile(require.resolve('./correlations')); loadTestFile(require.resolve('./entities')); From 9975c552da13e778b82ffa91d1a6fe1de8cac4a6 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Tue, 12 Nov 2024 14:25:09 +0100 Subject: [PATCH 057/100] [Logs] Deprecation warning for Logs Explorer and Logs Stream (#199652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Closes https://github.com/elastic/observability-dev/issues/4070 - Update the deprecation callouts to suggest that the user use Discover. - Replace the beta badge in Logs Explorer with a deprecation notice. - Mark the advanced setting to enable the log stream to be deprecated. Screenshot 2024-11-11 at 15 22 51 --------- Co-authored-by: Marco Antonio Ghiani Co-authored-by: Mike Birnstiehl <114418652+mdbirnstiehl@users.noreply.github.com> --- .../infra/common/ui_settings.ts | 7 ++++ .../components/logs_deprecation_callout.tsx | 37 +++++++--------- .../common/translations.ts | 24 ++++++++--- .../components/logs_explorer_top_nav_menu.tsx | 42 +++++++++++-------- .../translations/translations/fr-FR.json | 5 --- .../translations/translations/ja-JP.json | 5 --- .../translations/translations/zh-CN.json | 5 --- 7 files changed, 64 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/observability_solution/infra/common/ui_settings.ts b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts index 95f1ee0a44ba..9b8563076194 100644 --- a/x-pack/plugins/observability_solution/infra/common/ui_settings.ts +++ b/x-pack/plugins/observability_solution/infra/common/ui_settings.ts @@ -23,6 +23,13 @@ export const uiSettings: Record = { description: i18n.translate('xpack.infra.enableLogsStreamDescription', { defaultMessage: 'Enables the legacy Logs Stream application and dashboard panel. ', }), + deprecation: { + message: i18n.translate('xpack.infra.enableLogsStreamDeprecationWarning', { + defaultMessage: + 'Logs Stream is deprecated, and this setting will be removed in Kibana 9.0.', + }), + docLinksKey: 'generalSettings', + }, type: 'boolean', schema: schema.boolean(), requiresPageReload: true, diff --git a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx index 21e61c08d281..0edb8b9ab292 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/logs_deprecation_callout.tsx @@ -9,36 +9,28 @@ import { EuiCallOut } from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton } from '@elastic/eui'; -import { - AllDatasetsLocatorParams, - ALL_DATASETS_LOCATOR_ID, - DatasetLocatorParams, -} from '@kbn/deeplinks-observability'; import { getRouterLinkProps } from '@kbn/router-utils'; import useLocalStorage from 'react-use/lib/useLocalStorage'; - import { euiThemeVars } from '@kbn/ui-theme'; import { css } from '@emotion/css'; import { LocatorPublic } from '@kbn/share-plugin/common'; +import { DISCOVER_APP_LOCATOR, DiscoverAppLocatorParams } from '@kbn/discover-plugin/common'; import { useKibanaContextForPlugin } from '../hooks/use_kibana'; const pageConfigurations = { stream: { dismissalStorageKey: 'log_stream_deprecation_callout_dismissed', - message: i18n.translate('xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel', { + message: i18n.translate('xpack.infra.logsDeprecationCallout.stream.exploreWithDiscover', { defaultMessage: - 'The new Logs Explorer makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation. We recommend switching to Logs Explorer, as it will replace Logs Stream in a future version.', + 'Logs Stream and Logs Explorer are set to be deprecated. Switch to Discover which now includes their functionality plus more features, better performance, and more intuitive navigation. ', }), }, settings: { dismissalStorageKey: 'log_settings_deprecation_callout_dismissed', - message: i18n.translate( - 'xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel', - { - defaultMessage: - 'These settings only apply to the legacy Logs Stream app, and we do not recommend configuring them. Instead, use Logs Explorer which makes viewing and inspecting your logs easier with more features, better performance, and more intuitive navigation.', - } - ), + message: i18n.translate('xpack.infra.logsDeprecationCallout.settings.exploreWithDiscover', { + defaultMessage: + 'These settings only apply to the legacy Logs Stream app. Switch to Discover for the same functionality plus more features, better performance, and more intuitive navigation.', + }), }, }; @@ -60,10 +52,9 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) => const [isDismissed, setDismissed] = useLocalStorage(dismissalStorageKey, false); - const allDatasetLocator = - share.url.locators.get(ALL_DATASETS_LOCATOR_ID); + const discoverLocator = share.url.locators.get(DISCOVER_APP_LOCATOR); - if (isDismissed || !(allDatasetLocator && discover?.show && fleet?.read)) { + if (isDismissed || !(discoverLocator && discover?.show && fleet?.read)) { return null; } @@ -81,19 +72,19 @@ export const LogsDeprecationCallout = ({ page }: LogsDeprecationCalloutProps) =>

    {message}

    - {i18n.translate('xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel', { - defaultMessage: 'Try Logs Explorer', + {i18n.translate('xpack.infra.logsDeprecationCallout.goToDiscoverButtonLabel', { + defaultMessage: 'Go to Discover', })} ); }; -const getLogsExplorerLinkProps = (locator: LocatorPublic) => { +const getDiscoverLinkProps = (locator: LocatorPublic) => { return getRouterLinkProps({ href: locator.getRedirectUrl({}), onClick: () => locator.navigate({}), diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/common/translations.ts b/x-pack/plugins/observability_solution/observability_logs_explorer/common/translations.ts index f89c6eb47e6c..380bc3c3c5a2 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/common/translations.ts +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/common/translations.ts @@ -22,14 +22,26 @@ export const observabilityAppTitle = i18n.translate( } ); -export const betaBadgeTitle = i18n.translate('xpack.observabilityLogsExplorer.betaBadgeTitle', { - defaultMessage: 'Beta', -}); +export const deprecationBadgeTitle = i18n.translate( + 'xpack.observabilityLogsExplorer.deprecationBadgeTitle', + { + defaultMessage: 'Deprecation notice', + } +); + +export const deprecationBadgeDescription = i18n.translate( + 'xpack.observabilityLogsExplorer.deprecationBadgeDescription', + { + defaultMessage: + 'Logs Stream and Logs Explorer are set to be deprecated. Switch to Discover which now includes their functionality plus more features and better performance.', + } +); -export const betaBadgeDescription = i18n.translate( - 'xpack.observabilityLogsExplorer.betaBadgeDescription', +export const deprecationBadgeGuideline = i18n.translate( + 'xpack.observabilityLogsExplorer.deprecationBadgeGuideline', { - defaultMessage: 'This application is in beta and therefore subject to change.', + defaultMessage: + 'You can temporarily access Logs Stream by re-enabling it in Advanced Settings.', } ); diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/logs_explorer_top_nav_menu.tsx b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/logs_explorer_top_nav_menu.tsx index c3f91b3bf866..0020df30b870 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/logs_explorer_top_nav_menu.tsx +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/logs_explorer_top_nav_menu.tsx @@ -21,7 +21,11 @@ import { LogsExplorerTabs } from '@kbn/discover-plugin/public'; import React, { useEffect, useState } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { filter, take } from 'rxjs'; -import { betaBadgeDescription, betaBadgeTitle } from '../../common/translations'; +import { + deprecationBadgeDescription, + deprecationBadgeGuideline, + deprecationBadgeTitle, +} from '../../common/translations'; import { useKibanaContextForPlugin } from '../utils/use_kibana'; import { ConnectedDiscoverLink } from './discover_link'; import { FeedbackLink } from './feedback_link'; @@ -59,13 +63,7 @@ const ProjectTopNav = () => { `} > - + @@ -115,15 +113,6 @@ const ClassicTopNav = () => { margin-left: ${euiThemeVars.euiSizeM}; `} > - - - @@ -145,6 +134,9 @@ const ClassicTopNav = () => { + + + @@ -171,3 +163,19 @@ const ConditionalVerticalRule = ({ Component }: { Component: JSX.Element | null {Component} ); + +const DeprecationNoticeBadge = () => ( + + {deprecationBadgeDescription} +
    +
    + {deprecationBadgeGuideline} + + } + alignment="middle" + /> +); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 301635bbb962..2a66c10a6509 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24651,8 +24651,6 @@ "xpack.infra.logs.viewInContext.logsFromContainerTitle": "Les logs affichés proviennent du conteneur {container}", "xpack.infra.logs.viewInContext.logsFromFileTitle": "Les logs affichés proviennent du fichier {file} et de l'hôte {host}", "xpack.infra.logsDeprecationCallout.euiCallOut.discoverANewLogLabel": "Il existe une nouvelle (et meilleure) façon d'explorer vos logs !", - "xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel": "Le nouveau Logs Explorer facilite la visualisation et l'inspection de vos journaux et propose davantage de fonctionnalités, des performances supérieures ainsi qu’une navigation plus intuitive. Nous vous recommandons de passer à Logs Explorer, car il remplacera Logs Stream dans une version ultérieure.", - "xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel": "Essayer Logs Explorer", "xpack.infra.logsHeaderAddDataButtonLabel": "Ajouter des données", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "L'état d'au moins un champ du formulaire est non valide.", "xpack.infra.logSourceConfiguration.dataViewDescription": "Vue de données contenant les données de log", @@ -24685,7 +24683,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "Réessayer", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "Recherche d'entrées de log… (par ex. host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "Erreur de filtrage du log", - "xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel": "Ces paramètres s'appliquent uniquement à l'ancienne application Logs Stream et nous vous déconseillons de les configurer. Utilisez plutôt le nouveau Logs Explorer qui facilite la visualisation et l'inspection de vos logs et propose davantage de fonctionnalités, des performances supérieures ainsi qu'une navigation plus intuitive.", "xpack.infra.logsSettingsPage.loadingButtonLabel": "Chargement", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription": "La maintenance des panneaux de flux de logs n'est plus assurée. Essayez d'utiliser {savedSearchDocsLink} pour une visualisation similaire.", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel": "recherches enregistrées", @@ -34658,8 +34655,6 @@ "xpack.observabilityLogsExplorer.alertsPopover.createSLOMenuItem": "Créer un SLO", "xpack.observabilityLogsExplorer.alertsPopover.manageRulesMenuItem": "{canCreateRule, select, true{Gérer} other{Afficher}} les règles", "xpack.observabilityLogsExplorer.appTitle": "Explorateur de logs", - "xpack.observabilityLogsExplorer.betaBadgeDescription": "Il s'agit du stade bêta de l'application, qui est donc susceptible d'évoluer.", - "xpack.observabilityLogsExplorer.betaBadgeTitle": "Bêta", "xpack.observabilityLogsExplorer.createSlo": "Créer un SLO", "xpack.observabilityLogsExplorer.datasetQualityLinkTitle": "Ensembles de données", "xpack.observabilityLogsExplorer.discoverLinkTitle": "Ouvrir dans Discover", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 17fb0b2ff933..6ac1f1e800bf 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24625,8 +24625,6 @@ "xpack.infra.logs.viewInContext.logsFromContainerTitle": "表示されたログはコンテナー{container}から取得されました", "xpack.infra.logs.viewInContext.logsFromFileTitle": "表示されたログは、ファイル{file}およびホスト{host}から取得されました", "xpack.infra.logsDeprecationCallout.euiCallOut.discoverANewLogLabel": "ログの探索には、新しく、もっと効果的な方法があります。", - "xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel": "新しいログエクスプローラーには、追加機能、パフォーマンスの改善、さらに直感的なナビゲーションが導入され、ログの表示と調査がさらに簡単になりました。将来のバージョンでは、ログストリームに取って代わるため、ログエクスプローラーを使用することをお勧めします。", - "xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel": "ログエクスプローラーを試す", "xpack.infra.logsHeaderAddDataButtonLabel": "データの追加", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "1つ以上のフォームフィールドが無効な状態です。", "xpack.infra.logSourceConfiguration.dataViewDescription": "ログデータを含むデータビュー", @@ -24658,7 +24656,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "再試行", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "ログエントリーを検索中…(例:host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "ログフィルターエラー", - "xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel": "これらの設定はレガシーログストリームアプリにのみ適用されます。これらを構成することはお勧めしません。代わりに、新しいログエクスプローラーを使用してください。追加機能、パフォーマンスの改善、さらに直感的なナビゲーションが導入され、ログの表示と調査がさらに簡単になりました。", "xpack.infra.logsSettingsPage.loadingButtonLabel": "読み込み中", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription": "ログストリームパネルは管理されていません。{savedSearchDocsLink}を同様の視覚化に活用してください。", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel": "保存された検索", @@ -34628,8 +34625,6 @@ "xpack.observabilityLogsExplorer.alertsPopover.createSLOMenuItem": "SLOの作成", "xpack.observabilityLogsExplorer.alertsPopover.manageRulesMenuItem": "ルールを{canCreateRule, select, true{管理} other{表示}}", "xpack.observabilityLogsExplorer.appTitle": "ログエクスプローラー", - "xpack.observabilityLogsExplorer.betaBadgeDescription": "このアプリケーションはベータ版であるため、変更される場合があります。", - "xpack.observabilityLogsExplorer.betaBadgeTitle": "ベータ", "xpack.observabilityLogsExplorer.createSlo": "SLOの作成", "xpack.observabilityLogsExplorer.datasetQualityLinkTitle": "データセット", "xpack.observabilityLogsExplorer.discoverLinkTitle": "Discoverで開く", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4cb03c51d057..787ddd9a45d4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24204,8 +24204,6 @@ "xpack.infra.logs.viewInContext.logsFromContainerTitle": "显示的日志来自容器 {container}", "xpack.infra.logs.viewInContext.logsFromFileTitle": "显示的日志来自文件 {file} 和主机 {host}", "xpack.infra.logsDeprecationCallout.euiCallOut.discoverANewLogLabel": "这是浏览日志的更有效的新方法!", - "xpack.infra.logsDeprecationCallout.p.theNewLogsExplorerLabel": "新的日志浏览器具有更多功能,更优异的性能和更直观的导航,便于您更轻松地查看和检查日志。建议您切换到日志浏览器,因为它将在未来版本中替代日志流。", - "xpack.infra.logsDeprecationCallout.tryLogsExplorerButtonLabel": "试用日志浏览器", "xpack.infra.logsHeaderAddDataButtonLabel": "添加数据", "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "至少一个表单字段处于无效状态。", "xpack.infra.logSourceConfiguration.dataViewDescription": "包含日志数据的数据视图", @@ -24236,7 +24234,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "重试", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "搜索日志条目……(例如 host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "日志筛选错误", - "xpack.infra.logsSettingsDeprecationCallout.p.theNewLogsExplorerLabel": "这些设置仅适用于旧版日志流应用,不建议配置它们。相反,应使用具有更多功能,更优异的性能和更直观的导航,便于您更轻松地查看和检查日志的日志浏览器。", "xpack.infra.logsSettingsPage.loadingButtonLabel": "正在加载", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription": "将不再维护日志流面板。尝试将 {savedSearchDocsLink} 用于类似可视化。", "xpack.infra.logsStreamEmbeddable.deprecationWarningDescription.savedSearchesLinkLabel": "已保存的搜索", @@ -34107,8 +34104,6 @@ "xpack.observabilityLogsExplorer.alertsPopover.createSLOMenuItem": "创建 SLO", "xpack.observabilityLogsExplorer.alertsPopover.manageRulesMenuItem": "{canCreateRule, select, true{管理} other{查看}}规则", "xpack.observabilityLogsExplorer.appTitle": "日志浏览器", - "xpack.observabilityLogsExplorer.betaBadgeDescription": "此应用程序为公测版,因此可能会进行更改。", - "xpack.observabilityLogsExplorer.betaBadgeTitle": "公测版", "xpack.observabilityLogsExplorer.createSlo": "创建 SLO", "xpack.observabilityLogsExplorer.datasetQualityLinkTitle": "数据集", "xpack.observabilityLogsExplorer.discoverLinkTitle": "在 Discover 中打开", From 7e06f554363d2bf8fe5656062e34053284005943 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 12 Nov 2024 08:28:28 -0500 Subject: [PATCH 058/100] [Fleet] Fix install with streaming test (#199725) --- .../apis/epm/install_with_streaming.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts b/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts index 4fa0e485be2b..95968cd51976 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_with_streaming.ts @@ -36,8 +36,7 @@ export default function (providerContext: FtrProviderContext) { return res?._source?.['epm-packages'] as Installation; }; - // Failing: See https://github.com/elastic/kibana/issues/199701 - describe.skip('Installs a package using stream-based approach', () => { + describe('Installs a package using stream-based approach', () => { skipIfNoDockerRegistry(providerContext); before(async () => { @@ -50,7 +49,8 @@ export default function (providerContext: FtrProviderContext) { await uninstallPackage('security_detection_engine', '8.16.0'); }); it('should install security-rule assets from the package', async () => { - await installPackage('security_detection_engine', '8.16.0').expect(200); + // Force install to install an outdatded version + await installPackage('security_detection_engine', '8.16.0', { force: true }).expect(200); const installationSO = await getInstallationSavedObject('security_detection_engine'); expect(installationSO?.installed_kibana).toEqual( expect.arrayContaining([ From f9e8aa07b79e6d81d691a4f166c04f74335fdf7f Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 12 Nov 2024 08:28:54 -0500 Subject: [PATCH 059/100] [Fleet] Change uninstall tokens space when changing agent policies spaces (#199536) --- .../fleet/server/services/app_context.ts | 1 + .../security/uninstall_token_service/index.ts | 2 +- .../services/spaces/agent_policy.test.ts | 28 ++++++++ .../server/services/spaces/agent_policy.ts | 22 ++++++ .../change_space_agent_policies.ts | 68 ++++++++++++------- 5 files changed, 95 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index 7dccb7ba1dfe..da40f8ff7d81 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -240,6 +240,7 @@ class AppContextService { // soClient as kibana internal users, be careful on how you use it, security is not enabled return appContextService.getSavedObjects().getScopedClient(fakeRequest, { excludedExtensions: [SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID], + includedHiddenTypes: [UNINSTALL_TOKENS_SAVED_OBJECT_TYPE], }); } diff --git a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts index 0a44a3df0f29..1f58fcafd396 100644 --- a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts +++ b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts @@ -46,7 +46,7 @@ import { appContextService } from '../../app_context'; import { agentPolicyService, getAgentPolicySavedObjectType } from '../../agent_policy'; import { isSpaceAwarenessEnabled } from '../../spaces/helpers'; -interface UninstallTokenSOAttributes { +export interface UninstallTokenSOAttributes { policy_id: string; token: string; token_plain: string; diff --git a/x-pack/plugins/fleet/server/services/spaces/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/spaces/agent_policy.test.ts index ab69d4708c43..079148a6ae04 100644 --- a/x-pack/plugins/fleet/server/services/spaces/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/spaces/agent_policy.test.ts @@ -39,6 +39,22 @@ describe('updateAgentPolicySpaces', () => { jest .mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension()) .updateObjectsSpaces.mockResolvedValue({ objects: [] }); + + jest + .mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension()) + .find.mockResolvedValue({ + total: 1, + page: 1, + per_page: 100, + saved_objects: [ + { + id: 'token1', + attributes: { + namespaces: ['default'], + }, + } as any, + ], + }); }); it('does nothings if agent policy already in correct space', async () => { @@ -87,6 +103,18 @@ describe('updateAgentPolicySpaces', () => { ['default'], { namespace: 'default', refresh: 'wait_for' } ); + + expect( + jest.mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension()).bulkUpdate + ).toBeCalledWith([ + { + id: 'token1', + type: 'fleet-uninstall-tokens', + attributes: { + namespaces: ['test'], + }, + }, + ]); }); it('throw when trying to change space to a policy with reusable package policies', async () => { diff --git a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts index 2f8d5ff1b14c..e123ca442665 100644 --- a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts @@ -13,6 +13,8 @@ import { AGENTS_INDEX, AGENT_POLICY_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, + SO_SEARCH_LIMIT, + UNINSTALL_TOKENS_SAVED_OBJECT_TYPE, } from '../../../common/constants'; import { appContextService } from '../app_context'; @@ -22,6 +24,7 @@ import { packagePolicyService } from '../package_policy'; import { FleetError, HostedAgentPolicyRestrictionRelatedError } from '../../errors'; import { isSpaceAwarenessEnabled } from './helpers'; +import type { UninstallTokenSOAttributes } from '../security/uninstall_token_service'; export async function updateAgentPolicySpaces({ agentPolicyId, @@ -112,6 +115,25 @@ export async function updateAgentPolicySpaces({ } } + // Update uninstall tokens + const uninstallTokensRes = await soClient.find({ + perPage: SO_SEARCH_LIMIT, + type: UNINSTALL_TOKENS_SAVED_OBJECT_TYPE, + filter: `${UNINSTALL_TOKENS_SAVED_OBJECT_TYPE}.attributes.policy_id:"${agentPolicyId}"`, + }); + + if (uninstallTokensRes.total > 0) { + await soClient.bulkUpdate( + uninstallTokensRes.saved_objects.map((so) => ({ + id: so.id, + type: UNINSTALL_TOKENS_SAVED_OBJECT_TYPE, + attributes: { + namespaces: newSpaceIds, + }, + })) + ); + } + // Update fleet server index agents, enrollment api keys await esClient.updateByQuery({ index: ENROLLMENT_API_KEYS_INDEX, diff --git a/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts b/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts index 4d1d08ac7b35..df3aa6f8f257 100644 --- a/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts +++ b/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts @@ -109,18 +109,29 @@ export default function (providerContext: FtrProviderContext) { }) .catch(() => {}); }); - async function assertPolicyAvailableInSpace(spaceId?: string) { - await apiClient.getAgentPolicy(defaultSpacePolicy1.item.id, spaceId); + + async function assertPackagePolicyAvailableInSpace(spaceId?: string) { await apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId); + } + + async function assertPackagePolicyNotAvailableInSpace(spaceId?: string) { + await expectToRejectWithNotFound(() => + apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId) + ); + } + + async function assertAgentPolicyAvailableInSpace(policyId: string, spaceId?: string) { + await apiClient.getAgentPolicy(policyId, spaceId); const enrollmentApiKeys = await apiClient.getEnrollmentApiKeys(spaceId); - expect( - enrollmentApiKeys.items.find((item) => item.policy_id === defaultSpacePolicy1.item.id) - ).not.to.be(undefined); + expect(enrollmentApiKeys.items.find((item) => item.policy_id === policyId)).not.to.be( + undefined + ); const agents = await apiClient.getAgents(spaceId); - expect( - agents.items.filter((a) => a.policy_id === defaultSpacePolicy1.item.id).length - ).to.be(1); + expect(agents.items.filter((a) => a.policy_id === policyId).length).to.be(1); + + const uninstallTokens = await apiClient.getUninstallTokens(spaceId); + expect(uninstallTokens.items.filter((t) => t.policy_id === policyId).length).to.be(1); } async function assertEnrollemntApiKeysForSpace(spaceId?: string, policyIds?: string[]) { @@ -136,23 +147,19 @@ export default function (providerContext: FtrProviderContext) { expect([...foundPolicyIds].sort()).to.eql(policyIds?.sort()); } - async function assertPolicyNotAvailableInSpace(spaceId?: string) { - await expectToRejectWithNotFound(() => - apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId) - ); - await expectToRejectWithNotFound(() => - apiClient.getAgentPolicy(defaultSpacePolicy1.item.id, spaceId) - ); + async function assertAgentPolicyNotAvailableInSpace(policyId: string, spaceId?: string) { + await expectToRejectWithNotFound(() => apiClient.getAgentPolicy(policyId, spaceId)); const enrollmentApiKeys = await apiClient.getEnrollmentApiKeys(spaceId); - expect( - enrollmentApiKeys.items.find((item) => item.policy_id === defaultSpacePolicy1.item.id) - ).to.be(undefined); + expect(enrollmentApiKeys.items.find((item) => item.policy_id === policyId)).to.be( + undefined + ); const agents = await apiClient.getAgents(spaceId); - expect( - agents.items.filter((a) => a.policy_id === defaultSpacePolicy1.item.id).length - ).to.be(0); + expect(agents.items.filter((a) => a.policy_id === policyId).length).to.be(0); + + const uninstallTokens = await apiClient.getUninstallTokens(spaceId); + expect(uninstallTokens.items.filter((t) => t.policy_id === policyId).length).to.be(0); } async function assertAgentSpaces(agentId: string, expectedSpaces: string[]) { @@ -173,8 +180,11 @@ export default function (providerContext: FtrProviderContext) { space_ids: ['default', TEST_SPACE_1], }); - await assertPolicyAvailableInSpace(); - await assertPolicyAvailableInSpace(TEST_SPACE_1); + await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id); + await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id, TEST_SPACE_1); + + await assertPackagePolicyAvailableInSpace(); + await assertPackagePolicyAvailableInSpace(TEST_SPACE_1); await assertAgentSpaces(policy1AgentId, ['default', TEST_SPACE_1]); await assertAgentSpaces(policy2AgentId, ['default']); @@ -184,6 +194,9 @@ export default function (providerContext: FtrProviderContext) { defaultSpacePolicy2.item.id, ]); await assertEnrollemntApiKeysForSpace(TEST_SPACE_1, [defaultSpacePolicy1.item.id]); + // Ensure no side effect on other policies + await assertAgentPolicyAvailableInSpace(defaultSpacePolicy2.item.id); + await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy2.item.id, TEST_SPACE_1); }); it('should allow set policy in test space only', async () => { @@ -194,12 +207,17 @@ export default function (providerContext: FtrProviderContext) { space_ids: [TEST_SPACE_1], }); - await assertPolicyNotAvailableInSpace(); - await assertPolicyAvailableInSpace(TEST_SPACE_1); + await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy1.item.id); + await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id, TEST_SPACE_1); + await assertPackagePolicyAvailableInSpace(TEST_SPACE_1); + await assertPackagePolicyNotAvailableInSpace(); await assertAgentSpaces(policy1AgentId, [TEST_SPACE_1]); await assertAgentSpaces(policy2AgentId, ['default']); await assertEnrollemntApiKeysForSpace('default', [defaultSpacePolicy2.item.id]); await assertEnrollemntApiKeysForSpace(TEST_SPACE_1, [defaultSpacePolicy1.item.id]); + // Ensure no side effect on other policies + await assertAgentPolicyAvailableInSpace(defaultSpacePolicy2.item.id); + await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy2.item.id, TEST_SPACE_1); }); it('should not allow add policy to a space where user do not have access', async () => { From 4a2de76333063a1c6e68eeeaf4697753593928a5 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:17:12 +1100 Subject: [PATCH 060/100] Unauthorized route migration for routes owned by obs-ux-logs-team (#198349) ### Authz API migration for unauthorized routes This PR migrates unauthorized routes owned by your team to a new security configuration. Please refer to the documentation for more information: [Authorization API](https://docs.elastic.dev/kibana-dev-docs/key-concepts/security-api-authorization) ### **Before migration:** ```ts router.get({ path: '/api/path', ... }, handler); ``` ### **After migration:** ```ts router.get({ path: '/api/path', security: { authz: { enabled: false, reason: 'This route is opted out from authorization because ...', }, }, ... }, handler); ``` ### What to do next? 1. Review the changes in this PR. 2. Elaborate on the reasoning to opt-out of authorization. 3. Routes without a compelling reason to opt-out of authorization should plan to introduce them as soon as possible. 2. You might need to update your tests to reflect the new security configuration: - If you have snapshot tests that include the route definition. ## Any questions? If you have any questions or need help with API authorization, please reach out to the `@elastic/kibana-security` team. Co-authored-by: Elastic Machine Co-authored-by: Marco Antonio Ghiani --- .../server/routes/fields_metadata/find_fields_metadata.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts b/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts index 5e518618d98d..422c16a72684 100644 --- a/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts +++ b/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts @@ -24,6 +24,12 @@ export const initFindFieldsMetadataRoute = ({ .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: 'This route is opted out from authorization', + }, + }, validate: { request: { query: createValidationFunction(fieldsMetadataV1.findFieldsMetadataRequestQueryRT), From 06986e4a86a0fa3c3951fcb6b2ba34ebe2769820 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Tue, 12 Nov 2024 15:46:39 +0100 Subject: [PATCH 061/100] [Security Solution] Add Alert Suppression editable component (#198673) **Partially addresses:** https://github.com/elastic/kibana/issues/171520 ## Summary This PR adds is built on top of https://github.com/elastic/kibana/pull/193828 and https://github.com/elastic/kibana/pull/196948 and adds an Alert Suppression editable component for Three Way Diff tab's final edit side of the upgrade prebuilt rule workflow. ## Details https://github.com/elastic/kibana/issues/171520 required adding editable components for each field diffable rule field. Alert Suppression edit component was extracted from Define Rule Step Component into a separate reusable component. To simplify the logic it was split into common Alert Suppression and Threshold Alert Suppression since the latter is a specific use case. ## Caveats Upgrade prebuilt rules workflow is quite different from rule creation and editing. In create and edit rule forms users are capable to change any field at their will. Upgrade prebuilt rules workflow allow to modify only specific fields having diff in the current rule upgrade. There are fields which depend on each other. In particular Alert Suppression isn't supported for EQL sequence though it's addressed in https://github.com/elastic/kibana/pull/189725. - Alert Suppression editable component in Three Way Diff workflow isn't disabled EQL sequence rule queries. Alert suppression support for rules with EQL sequence queries is implemented in https://github.com/elastic/kibana/pull/189725. - Machine learning rule type require running selected machine learning jobs otherwise input could be disabled in case of there are no fields to pick from otherwise a warning message below the combobox is shown. ## How to test The simplest way to test is via patching installed prebuilt rules via Rule Patch API. Please follow steps below - Enable Prebuilt rule customization feature by adding a `prebuiltRulesCustomizationEnabled` feature flag - Run Kibana locally - Install a prebuilt rule, e.g. `Potential Code Execution via Postgresql` with rule_id `2a692072-d78d-42f3-a48a-775677d79c4e` - Patch the installed rule by running a query below ```bash curl -X PATCH --user elastic:changeme -H 'Content-Type: application/json' -H 'kbn-xsrf: 123' -H "elastic-api-version: 2023-10-31" -d '{"rule_id":"2a692072-d78d-42f3-a48a-775677d79c4e","version":1,"alert_suppression":{"group_by":["host.name"]}}' http://localhost:5601/kbn/api/detection_engine/rules ``` - Open `Detection Rules (SIEM)` Page -> `Rule Updates` -> click on `Potential Code Execution via Postgresql` rule -> expand `EQL Query` to see EQL Query -> press `Edit` button ## Screenshots Custom query prebuilt rule (UI looks similar for EQL, Indicator Match, New Terms and ES|QL rule types) ![image](https://github.com/user-attachments/assets/86015d5b-e252-4d0b-9aa3-fc14679a493b) Machine learning prebuilt rule with a diff in alert suppression ![image](https://github.com/user-attachments/assets/210246cd-27fd-4976-befc-dee023101ec9) Threshold prebuilt rule ![image](https://github.com/user-attachments/assets/44b0c1bc-4134-4d58-bd9a-e8e2d4c50802) --- oas_docs/output/kibana.serverless.yaml | 13 +- oas_docs/output/kibana.yaml | 13 +- .../rule_schema/common_attributes.gen.ts | 7 +- .../rule_schema/common_attributes.schema.yaml | 13 +- ...ections_api_2023_10_31.bundled.schema.yaml | 12 +- ...ections_api_2023_10_31.bundled.schema.yaml | 12 +- .../markdown_editor/plugins/index.ts | 4 +- .../plugins/insight/index.test.tsx | 2 +- .../markdown_editor/plugins/insight/index.tsx | 6 +- .../plugins/osquery/plugin.tsx | 2 +- .../plugins/timeline/plugin.tsx | 2 +- .../common/hooks/use_upselling.test.tsx | 4 +- .../public/common/hooks/use_upselling.ts | 4 +- .../public/common/test/eui/combobox.ts | 79 +++++ .../components/alert_suppression_edit.tsx | 64 ++++ .../missing_fields_strategy_selector.tsx | 61 ++++ .../suppression_duration_selector.tsx | 140 +++++++++ .../suppression_fields_selector.tsx | 46 +++ .../components/suppression_info_icon.tsx} | 21 +- .../components/translations.ts | 94 ++++++ .../constants/default_duration.ts | 17 ++ .../constants/fields.ts | 13 + .../alert_suppression_edit/index.ts | 11 + .../alert_suppression_edit/test_helpers.ts | 72 +++++ .../components/duration_input/index.tsx | 82 ++--- .../components/duration_input/translations.ts | 7 + .../related_integrations.test.tsx | 88 +----- .../related_integrations/test_helpers.ts | 47 +++ .../fields.ts | 8 + .../threshold_alert_suppression_edit/index.ts | 9 + .../threshold_alert_suppression_edit.tsx | 63 ++++ .../translations.tsx | 32 ++ ...a_views.ts => use_data_view_list_items.ts} | 2 +- .../data_view_selector_field.test.tsx | 14 +- .../data_view_selector_field.tsx | 4 +- ...a_views.ts => use_data_view_list_items.ts} | 2 +- .../components/description_step/helpers.tsx | 8 +- .../description_step/index.test.tsx | 63 ++-- .../components/description_step/index.tsx | 23 +- .../description_step/translations.ts | 4 +- .../components/multi_select_fields/index.tsx | 18 +- .../components/step_about_rule/index.test.tsx | 25 +- .../step_define_rule/index.test.tsx | 208 +++++-------- .../components/step_define_rule/index.tsx | 283 +++--------------- .../components/step_define_rule/schema.tsx | 67 ++--- .../step_define_rule/translations.tsx | 15 +- .../use_persistent_alert_suppression_state.ts | 77 +++++ .../components/step_define_rule/utils.ts | 11 +- .../rule_creation_ui/pages/form.test.ts | 3 +- .../pages/rule_creation/helpers.test.ts | 29 +- .../pages/rule_creation/helpers.ts | 25 +- .../pages/rule_creation/index.tsx | 14 +- .../pages/rule_editing/index.tsx | 4 +- ...rt_suppression_fields_validator_factory.ts | 25 ++ .../custom_query_rule_field_edit.tsx | 3 + .../final_edit/eql_rule_field_edit.tsx | 3 + .../final_edit/esql_rule_field_edit.tsx | 23 ++ .../fields/alert_suppression/form_schema.ts | 37 +++ .../fields/alert_suppression/index.ts | 8 + .../suppression_edit_adapter.tsx | 81 +++++ .../suppression_edit_form.tsx | 70 +++++ .../fields/alert_suppression/translations.tsx | 38 +++ .../data_source/data_source_edit_form.tsx | 16 +- .../data_source_type_selector_field.tsx | 13 +- .../fields/data_source/index_pattern_edit.tsx | 2 +- .../fields/data_source/translations.tsx | 18 +- .../final_edit/fields/hooks/use_data_view.ts | 59 ++++ .../hooks/use_diffable_rule_data_view.ts | 33 ++ .../fields/kql_query/kql_query_edit.tsx | 54 +--- .../form_schema.ts | 33 ++ .../threshold_alert_suppression/index.ts | 8 + .../suppression_edit_form.tsx | 70 +++++ .../three_way_diff/final_edit/final_edit.tsx | 10 +- .../machine_learning_rule_field_edit.tsx | 23 ++ .../final_edit/new_terms_rule_field_edit.tsx | 3 + .../saved_query_rule_field_edit.tsx | 3 + .../threat_match_rule_field_edit.tsx | 3 + .../final_edit/threshold_rule_field_edit.tsx | 3 + .../components/rules_table/__mocks__/mock.ts | 30 +- .../detection_engine/rules/helpers.test.tsx | 29 +- .../pages/detection_engine/rules/helpers.tsx | 24 +- .../pages/detection_engine/rules/types.ts | 19 +- .../pages/detection_engine/rules/utils.ts | 23 +- .../plugins/security_solution/tsconfig.json | 1 + .../translations/translations/fr-FR.json | 8 - .../translations/translations/ja-JP.json | 8 - .../translations/translations/zh-CN.json | 8 - .../common_flows_supression_ess_basic.cy.ts | 7 +- .../machine_learning_rule_suppression.cy.ts | 4 +- .../rule_edit/esql_rule.cy.ts | 8 +- .../rule_edit/indicator_match_rule.cy.ts | 9 +- .../rule_edit/machine_learning_rule.cy.ts | 15 +- .../rule_edit/new_terms_rule.cy.ts | 9 +- .../rule_edit/threshold_rule.cy.ts | 11 +- .../cypress/screens/create_new_rule.ts | 13 +- .../cypress/tasks/create_new_rule.ts | 7 +- 96 files changed, 1942 insertions(+), 877 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/test/eui/combobox.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/alert_suppression_edit.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/missing_fields_strategy_selector.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_duration_selector.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_fields_selector.tsx rename x-pack/plugins/security_solution/public/detection_engine/{rule_creation_ui/components/suppression_info_icon/index.tsx => rule_creation/components/alert_suppression_edit/components/suppression_info_icon.tsx} (80%) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/default_duration.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/fields.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/index.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/test_helpers.ts rename x-pack/plugins/security_solution/public/detection_engine/{rule_creation_ui => rule_creation}/components/duration_input/index.tsx (68%) rename x-pack/plugins/security_solution/public/detection_engine/{rule_creation_ui => rule_creation}/components/duration_input/translations.ts (82%) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/test_helpers.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/fields.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/index.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/threshold_alert_suppression_edit.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/translations.tsx rename x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/{use_data_views.ts => use_data_view_list_items.ts} (81%) rename x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/{use_data_views.ts => use_data_view_list_items.ts} (95%) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_persistent_alert_suppression_state.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/validators/alert_suppression_fields_validator_factory.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/esql_rule_field_edit.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/alert_suppression/form_schema.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/alert_suppression/index.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/alert_suppression/suppression_edit_adapter.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/alert_suppression/suppression_edit_form.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/alert_suppression/translations.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/hooks/use_data_view.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/hooks/use_diffable_rule_data_view.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threshold_alert_suppression/form_schema.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threshold_alert_suppression/index.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/threshold_alert_suppression/suppression_edit_form.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/machine_learning_rule_field_edit.tsx diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 5f18154db449..95c201de052c 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -39976,17 +39976,20 @@ components: type: object properties: unit: - enum: - - s - - m - - h - type: string + $ref: >- + #/components/schemas/Security_Detections_API_AlertSuppressionDurationUnit value: minimum: 1 type: integer required: - value - unit + Security_Detections_API_AlertSuppressionDurationUnit: + enum: + - s + - m + - h + type: string Security_Detections_API_AlertSuppressionGroupBy: items: type: string diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 133dede5fcd0..8b4953b4149a 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -48220,17 +48220,20 @@ components: type: object properties: unit: - enum: - - s - - m - - h - type: string + $ref: >- + #/components/schemas/Security_Detections_API_AlertSuppressionDurationUnit value: minimum: 1 type: integer required: - value - unit + Security_Detections_API_AlertSuppressionDurationUnit: + enum: + - s + - m + - h + type: string Security_Detections_API_AlertSuppressionGroupBy: items: type: string diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index 1bf3bfb5e244..2d722e7d5076 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -555,10 +555,15 @@ export const RuleExceptionList = z.object({ namespace_type: z.enum(['agnostic', 'single']), }); +export type AlertSuppressionDurationUnit = z.infer; +export const AlertSuppressionDurationUnit = z.enum(['s', 'm', 'h']); +export type AlertSuppressionDurationUnitEnum = typeof AlertSuppressionDurationUnit.enum; +export const AlertSuppressionDurationUnitEnum = AlertSuppressionDurationUnit.enum; + export type AlertSuppressionDuration = z.infer; export const AlertSuppressionDuration = z.object({ value: z.number().int().min(1), - unit: z.enum(['s', 'm', 'h']), + unit: AlertSuppressionDurationUnit, }); /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index 088153cca488..029a71b9e0a1 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -581,6 +581,13 @@ components: - type - namespace_type + AlertSuppressionDurationUnit: + type: string + enum: + - s + - m + - h + AlertSuppressionDuration: type: object properties: @@ -588,11 +595,7 @@ components: type: integer minimum: 1 unit: - type: string - enum: - - s - - m - - h + $ref: '#/components/schemas/AlertSuppressionDurationUnit' required: - value - unit diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml index ebd4c9328009..7e8d7a61bff2 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml @@ -1560,17 +1560,19 @@ components: type: object properties: unit: - enum: - - s - - m - - h - type: string + $ref: '#/components/schemas/AlertSuppressionDurationUnit' value: minimum: 1 type: integer required: - value - unit + AlertSuppressionDurationUnit: + enum: + - s + - m + - h + type: string AlertSuppressionGroupBy: items: type: string diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml index 30a7ae3ea343..58456e71140a 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml @@ -850,17 +850,19 @@ components: type: object properties: unit: - enum: - - s - - m - - h - type: string + $ref: '#/components/schemas/AlertSuppressionDurationUnit' value: minimum: 1 type: integer required: - value - unit + AlertSuppressionDurationUnit: + enum: + - s + - m + - h + type: string AlertSuppressionGroupBy: items: type: string diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts index 607ed6d94959..b6067ac21eb1 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/index.ts @@ -24,8 +24,8 @@ export const uiPlugins = ({ insightsUpsellingMessage, interactionsUpsellingMessage, }: { - insightsUpsellingMessage: string | null; - interactionsUpsellingMessage: string | null; + insightsUpsellingMessage?: string; + interactionsUpsellingMessage?: string; }) => { const currentPlugins = nonStatefulUiPlugins.map((plugin) => plugin.name); const insightPluginWithLicense = insightMarkdownPlugin.plugin({ diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.test.tsx index 37d4e004edf5..026e070f320d 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.test.tsx @@ -135,7 +135,7 @@ describe('plugin', () => { }); it('show investigate message when insightsUpsellingMessage is not provided', () => { - const result = plugin({ insightsUpsellingMessage: null }); + const result = plugin({ insightsUpsellingMessage: undefined }); expect(result.button.label).toEqual('Investigate'); }); diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.tsx index e8fa43d0a189..794b17a21920 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/insight/index.tsx @@ -541,11 +541,7 @@ const exampleInsight = `${insightPrefix}{ ] }}`; -export const plugin = ({ - insightsUpsellingMessage, -}: { - insightsUpsellingMessage: string | null; -}) => { +export const plugin = ({ insightsUpsellingMessage }: { insightsUpsellingMessage?: string }) => { return { name: 'insights', button: { diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/plugin.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/plugin.tsx index 6a37280f9ef2..67b3f9e2b4f8 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/plugin.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/osquery/plugin.tsx @@ -161,7 +161,7 @@ const OsqueryEditor = React.memo(OsqueryEditorComponent); export const plugin = ({ interactionsUpsellingMessage, }: { - interactionsUpsellingMessage: string | null; + interactionsUpsellingMessage?: string; }) => { return { name: 'osquery', diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx index 40b2fba4b84d..4d5b2e14e0d9 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/plugin.tsx @@ -78,7 +78,7 @@ const TimelineEditor = memo(TimelineEditorComponent); export const plugin = ({ interactionsUpsellingMessage, }: { - interactionsUpsellingMessage: string | null; + interactionsUpsellingMessage?: string; }): EuiMarkdownEditorUiPlugin => { return { name: ID, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx index ee783bcd19e3..c18a6282eb37 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.test.tsx @@ -71,7 +71,7 @@ describe('use_upselling', () => { expect(result.all.length).toBe(1); // assert that it should not cause unnecessary re-renders }); - test('useUpsellingMessage returns null when upsellingMessageId not found', () => { + test('useUpsellingMessage returns undefined when upsellingMessageId not found', () => { const emptyMessages = {}; mockUpselling.setPages(emptyMessages); @@ -81,6 +81,6 @@ describe('use_upselling', () => { wrapper: RenderWrapper, } ); - expect(result.current).toBe(null); + expect(result.current).toBeUndefined(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts index 8ef01b5b56a2..9f452bb4f287 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_upselling.ts @@ -22,11 +22,11 @@ export const useUpsellingComponent = (id: UpsellingSectionId): React.ComponentTy return useMemo(() => upsellingSections?.get(id) ?? null, [id, upsellingSections]); }; -export const useUpsellingMessage = (id: UpsellingMessageId): string | null => { +export const useUpsellingMessage = (id: UpsellingMessageId): string | undefined => { const upselling = useUpsellingService(); const upsellingMessages = useObservable(upselling.messages$, upselling.getMessagesValue()); - return useMemo(() => upsellingMessages?.get(id) ?? null, [id, upsellingMessages]); + return useMemo(() => upsellingMessages?.get(id), [id, upsellingMessages]); }; export const useUpsellingPage = (pageName: SecurityPageName): React.ComponentType | null => { diff --git a/x-pack/plugins/security_solution/public/common/test/eui/combobox.ts b/x-pack/plugins/security_solution/public/common/test/eui/combobox.ts new file mode 100644 index 000000000000..dad99a3c426d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/test/eui/combobox.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, fireEvent, waitFor } from '@testing-library/react'; + +export function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { + fireEvent.click(comboBoxToggleButton); + + return waitFor(() => { + const listWithOptionsElement = document.querySelector('[role="listbox"]'); + const emptyListElement = document.querySelector('.euiComboBoxOptionsList__empty'); + + expect(listWithOptionsElement || emptyListElement).toBeInTheDocument(); + }); +} + +type SelectEuiComboBoxOptionParameters = + | { + comboBoxToggleButton: HTMLElement; + optionIndex: number; + optionText?: undefined; + } + | { + comboBoxToggleButton: HTMLElement; + optionText: string; + optionIndex?: undefined; + }; + +export function selectEuiComboBoxOption({ + comboBoxToggleButton, + optionIndex, + optionText, +}: SelectEuiComboBoxOptionParameters): Promise { + return act(async () => { + await showEuiComboBoxOptions(comboBoxToggleButton); + + const options = Array.from( + document.querySelectorAll('[data-test-subj*="comboBoxOptionsList"] [role="option"]') + ); + + if (typeof optionText === 'string') { + const optionToSelect = options.find((option) => option.textContent === optionText); + + if (optionToSelect) { + fireEvent.click(optionToSelect); + } else { + throw new Error( + `Could not find option with text "${optionText}". Available options: ${options + .map((option) => option.textContent) + .join(', ')}` + ); + } + } else { + fireEvent.click(options[optionIndex]); + } + }); +} + +export function selectFirstEuiComboBoxOption({ + comboBoxToggleButton, +}: { + comboBoxToggleButton: HTMLElement; +}): Promise { + return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); +} + +export function clearEuiComboBoxSelection({ + clearButton, +}: { + clearButton: HTMLElement; +}): Promise { + return act(async () => { + fireEvent.click(clearButton); + }); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/alert_suppression_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/alert_suppression_edit.tsx new file mode 100644 index 000000000000..5c6099529e92 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/alert_suppression_edit.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { EuiPanel, EuiText, EuiToolTip } from '@elastic/eui'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import { useFormData } from '../../../../../shared_imports'; +import { MissingFieldsStrategySelector } from './missing_fields_strategy_selector'; +import { SuppressionDurationSelector } from './suppression_duration_selector'; +import { SuppressionFieldsSelector } from './suppression_fields_selector'; +import { ALERT_SUPPRESSION_FIELDS_FIELD_NAME } from '../constants/fields'; + +interface AlertSuppressionEditProps { + suppressibleFields: DataViewFieldBase[]; + labelAppend?: React.ReactNode; + disabled?: boolean; + disabledText?: string; + warningText?: string; +} + +export const AlertSuppressionEdit = memo(function AlertSuppressionEdit({ + suppressibleFields, + labelAppend, + disabled, + disabledText, + warningText, +}: AlertSuppressionEditProps): JSX.Element { + const [{ [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: suppressionFields }] = useFormData<{ + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: string[]; + }>({ + watch: ALERT_SUPPRESSION_FIELDS_FIELD_NAME, + }); + const hasSelectedFields = suppressionFields?.length > 0; + const content = ( + <> + + {warningText && ( + + {warningText} + + )} + + + + + + ); + + return disabled && disabledText ? ( + + {content} + + ) : ( + content + ); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/missing_fields_strategy_selector.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/missing_fields_strategy_selector.tsx new file mode 100644 index 000000000000..a7b38843a61c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/missing_fields_strategy_selector.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import type { EuiFormRowProps, EuiRadioGroupOption, EuiRadioGroupProps } from '@elastic/eui'; +import { RadioGroupField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { AlertSuppressionMissingFieldsStrategyEnum } from '../../../../../../common/api/detection_engine'; +import { UseField } from '../../../../../shared_imports'; +import { SuppressionInfoIcon } from './suppression_info_icon'; +import { ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME } from '../constants/fields'; +import * as i18n from './translations'; + +interface MissingFieldsStrategySelectorProps { + disabled?: boolean; +} + +export function MissingFieldsStrategySelector({ + disabled, +}: MissingFieldsStrategySelectorProps): JSX.Element { + const radioFieldProps: Partial = useMemo( + () => ({ + options: ALERT_SUPPRESSION_MISSING_FIELDS_STRATEGY_OPTIONS, + 'data-test-subj': 'suppressionMissingFieldsOptions', + disabled, + }), + [disabled] + ); + + return ( + + ); +} + +const EUI_FORM_ROW_PROPS: Partial = { + label: ( + + {i18n.ALERT_SUPPRESSION_MISSING_FIELDS_LABEL} + + ), + 'data-test-subj': 'alertSuppressionMissingFields', +}; + +const ALERT_SUPPRESSION_MISSING_FIELDS_STRATEGY_OPTIONS: EuiRadioGroupOption[] = [ + { + id: AlertSuppressionMissingFieldsStrategyEnum.suppress, + label: i18n.ALERT_SUPPRESSION_MISSING_FIELDS_SUPPRESS_OPTION, + }, + { + id: AlertSuppressionMissingFieldsStrategyEnum.doNotSuppress, + label: i18n.ALERT_SUPPRESSION_MISSING_FIELDS_DO_NOT_SUPPRESS_OPTION, + }, +]; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_duration_selector.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_duration_selector.tsx new file mode 100644 index 000000000000..7cf5eeb3018b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_duration_selector.tsx @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useEffect } from 'react'; +import { EuiFormRow, EuiRadioGroup, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; +import type { FieldHook } from '../../../../../shared_imports'; +import { UseMultiFields } from '../../../../../shared_imports'; +import { AlertSuppressionDurationType } from '../../../../../detections/pages/detection_engine/rules/types'; +import { DurationInput } from '../../duration_input'; +import { + ALERT_SUPPRESSION_DURATION_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME, +} from '../constants/fields'; +import * as i18n from './translations'; + +interface AlertSuppressionDurationProps { + onlyPerTimePeriod?: boolean; + onlyPerTimePeriodReasonMessage?: string; + disabled?: boolean; +} + +export function SuppressionDurationSelector({ + onlyPerTimePeriod, + onlyPerTimePeriodReasonMessage, + disabled, +}: AlertSuppressionDurationProps): JSX.Element { + return ( + + + fields={{ + suppressionDurationSelector: { + path: ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, + }, + suppressionDurationValue: { + path: `${ALERT_SUPPRESSION_DURATION_FIELD_NAME}.${ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME}`, + }, + suppressionDurationUnit: { + path: `${ALERT_SUPPRESSION_DURATION_FIELD_NAME}.${ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME}`, + }, + }} + > + {({ suppressionDurationSelector, suppressionDurationValue, suppressionDurationUnit }) => ( + + )} + + + ); +} + +interface SuppressionDurationSelectorFieldsProps { + suppressionDurationSelectorField: FieldHook; + suppressionDurationValueField: FieldHook; + suppressionDurationUnitField: FieldHook; + onlyPerTimePeriod?: boolean; + onlyPerTimePeriodReasonMessage?: string; + disabled?: boolean; +} + +const SuppressionDurationSelectorFields = memo(function SuppressionDurationSelectorFields({ + suppressionDurationSelectorField, + suppressionDurationValueField, + suppressionDurationUnitField, + onlyPerTimePeriod = false, + onlyPerTimePeriodReasonMessage, + disabled, +}: SuppressionDurationSelectorFieldsProps): JSX.Element { + const { euiTheme } = useEuiTheme(); + const { value: durationType, setValue: setDurationType } = suppressionDurationSelectorField; + + useEffect(() => { + if (onlyPerTimePeriod && durationType !== AlertSuppressionDurationType.PerTimePeriod) { + setDurationType(AlertSuppressionDurationType.PerTimePeriod); + } + }, [onlyPerTimePeriod, durationType, setDurationType]); + + return ( + <> + + <> {i18n.ALERT_SUPPRESSION_DURATION_PER_RULE_EXECUTION_OPTION} +
    + ) : ( + i18n.ALERT_SUPPRESSION_DURATION_PER_RULE_EXECUTION_OPTION + ), + disabled: onlyPerTimePeriod ? true : disabled, + }, + { + id: AlertSuppressionDurationType.PerTimePeriod, + disabled, + label: i18n.ALERT_SUPPRESSION_DURATION_PER_TIME_PERIOD_OPTION, + }, + ]} + onChange={(id) => { + suppressionDurationSelectorField.setValue(id); + }} + data-test-subj="alertSuppressionDurationOptions" + /> +
    + +
    + + ); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_fields_selector.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_fields_selector.tsx new file mode 100644 index 000000000000..72eea027288f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_fields_selector.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFormRow } from '@elastic/eui'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import { UseField } from '../../../../../shared_imports'; +import { MultiSelectFieldsAutocomplete } from '../../../../rule_creation_ui/components/multi_select_fields'; +import { ALERT_SUPPRESSION_FIELDS_FIELD_NAME } from '../constants/fields'; +import * as i18n from './translations'; + +interface SuppressionFieldsSelectorProps { + suppressibleFields: DataViewFieldBase[]; + labelAppend?: React.ReactNode; + disabled?: boolean; +} + +export function SuppressionFieldsSelector({ + suppressibleFields, + labelAppend, + disabled, +}: SuppressionFieldsSelectorProps): JSX.Element { + return ( + + <> + + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/suppression_info_icon/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_info_icon.tsx similarity index 80% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/suppression_info_icon/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_info_icon.tsx index bb3b0db1ccda..466600dd394d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/suppression_info_icon/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/suppression_info_icon.tsx @@ -5,28 +5,25 @@ * 2.0. */ -import React, { useState } from 'react'; +import React from 'react'; import { EuiLink, EuiPopover, EuiText, EuiButtonIcon } from '@elastic/eui'; +import { useBoolean } from '@kbn/react-hooks'; import { FormattedMessage } from '@kbn/i18n-react'; - -import { useKibana } from '../../../../common/lib/kibana'; +import { useKibana } from '../../../../../common/lib/kibana'; const POPOVER_WIDTH = 320; /** * Icon and popover that gives hint to users how suppression for missing fields work */ -const SuppressionInfoIconComponent = () => { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); +export function SuppressionInfoIcon(): JSX.Element { + const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false); const { docLinks } = useKibana().services; - const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen); - const closePopover = () => setIsPopoverOpen(false); - const button = ( ); @@ -59,8 +56,4 @@ const SuppressionInfoIconComponent = () => { ); -}; - -export const SuppressionInfoIcon = React.memo(SuppressionInfoIconComponent); - -SuppressionInfoIcon.displayName = 'SuppressionInfoIcon'; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/translations.ts new file mode 100644 index 000000000000..8da7d435adfe --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/components/translations.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const ALERT_SUPPRESSION_SUPPRESS_BY_FIELD_LABEL = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.fieldsSelector.label', + { + defaultMessage: 'Suppress alerts by', + } +); + +export const ALERT_SUPPRESSION_SUPPRESS_BY_FIELD_HELP_TEXT = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.suppressByFields.helpText', + { + defaultMessage: 'Select field(s) to use for suppressing extra alerts', + } +); + +export const ALERT_SUPPRESSION_DURATION_PER_RULE_EXECUTION_OPTION = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.suppressionDuration.perRuleExecutionOption', + { + defaultMessage: 'Per rule execution', + } +); + +export const ALERT_SUPPRESSION_DURATION_PER_TIME_PERIOD_OPTION = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.suppressionDuration.perTimePeriodOption', + { + defaultMessage: 'Per time period', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_LABEL = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.missingFields.label', + { + defaultMessage: 'If a suppression field is missing', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_SUPPRESS_OPTION = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.missingFields.suppressOption', + { + defaultMessage: 'Suppress and group alerts for events with missing fields', + } +); + +export const ALERT_SUPPRESSION_MISSING_FIELDS_DO_NOT_SUPPRESS_OPTION = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.missingFields.doNotSuppressOption', + { + defaultMessage: 'Do not suppress alerts for events with missing fields', + } +); + +export const ALERT_SUPPRESSION_NOT_SUPPORTED_FOR_EQL_SEQUENCE = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.notSupportedForEqlSequence', + { + defaultMessage: 'Suppression is not supported for EQL sequence queries', + } +); + +export const MACHINE_LEARNING_SUPPRESSION_FIELDS_LOADING = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.machineLearningSuppressionFieldsLoading', + { + defaultMessage: 'Machine Learning suppression fields are loading', + } +); + +export const MACHINE_LEARNING_NO_SUPPRESSION_FIELDS = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.machineLearningNoSuppressionFields', + { + defaultMessage: + 'Unable to load machine Learning suppression fields, start relevant Machine Learning jobs.', + } +); + +export const ESQL_SUPPRESSION_FIELDS_LOADING = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.esqlFieldsLoading', + { + defaultMessage: 'ES|QL suppression fields are loading', + } +); + +export const MACHINE_LEARNING_SUPPRESSION_INCOMPLETE_LABEL = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.alertSuppression.machineLearningSuppressionIncomplete', + { + defaultMessage: + 'This list of fields might be incomplete as some Machine Learning jobs are not running. Start all relevant jobs for a complete list.', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/default_duration.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/default_duration.ts new file mode 100644 index 000000000000..6e06d0d67031 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/default_duration.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AlertSuppressionDurationUnitEnum } from '../../../../../../common/api/detection_engine'; +import { + ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME, +} from './fields'; + +export const ALERT_SUPPRESSION_DEFAULT_DURATION = { + [ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME]: 5, + [ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME]: AlertSuppressionDurationUnitEnum.m, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/fields.ts new file mode 100644 index 000000000000..42a0583e9051 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/constants/fields.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ALERT_SUPPRESSION_FIELDS_FIELD_NAME = 'alertSuppressionFields' as const; +export const ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME = 'alertSuppressionDurationType' as const; +export const ALERT_SUPPRESSION_DURATION_FIELD_NAME = 'alertSuppressionDuration' as const; +export const ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME = 'value' as const; +export const ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME = 'unit' as const; +export const ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME = 'alertSuppressionMissingFields' as const; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/index.ts new file mode 100644 index 000000000000..a97e74726e3c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './components/alert_suppression_edit'; +export * from './components/suppression_duration_selector'; +export * from './constants/fields'; +export * from './constants/default_duration'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/test_helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/test_helpers.ts new file mode 100644 index 000000000000..b7d6c4003e93 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/alert_suppression_edit/test_helpers.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import { act, fireEvent, waitFor, within, screen } from '@testing-library/react'; +import type { AlertSuppressionDurationUnit } from '../../../../../common/api/detection_engine'; +import { selectEuiComboBoxOption } from '../../../../common/test/eui/combobox'; + +const COMBO_BOX_TOGGLE_BUTTON_TEST_ID = 'comboBoxToggleListButton'; + +export async function setSuppressionFields(fieldNames: string[]): Promise { + const getAlertSuppressionFieldsComboBoxToggleButton = () => + within(screen.getByTestId('alertSuppressionInput')).getByTestId( + COMBO_BOX_TOGGLE_BUTTON_TEST_ID + ); + + await waitFor(() => { + expect(getAlertSuppressionFieldsComboBoxToggleButton()).toBeInTheDocument(); + }); + + for (const fieldName of fieldNames) { + await selectEuiComboBoxOption({ + comboBoxToggleButton: getAlertSuppressionFieldsComboBoxToggleButton(), + optionText: fieldName, + }); + } +} + +export function expectSuppressionFields(fieldNames: string[]): void { + for (const fieldName of fieldNames) { + expect( + within(screen.getByTestId('alertSuppressionInput')).getByTitle(fieldName) + ).toBeInTheDocument(); + } +} + +export function setDurationType(value: 'Per rule execution' | 'Per time period'): void { + act(() => { + fireEvent.click(within(screen.getByTestId('alertSuppressionDuration')).getByLabelText(value)); + }); +} + +export function setDuration(value: number, unit: AlertSuppressionDurationUnit): void { + act(() => { + fireEvent.input( + within(screen.getByTestId('alertSuppressionDuration')).getByTestId('interval'), + { + target: { value: value.toString() }, + } + ); + + fireEvent.change( + within(screen.getByTestId('alertSuppressionDuration')).getByTestId('timeType'), + { + target: { value: unit }, + } + ); + }); +} + +export function expectDuration(value: number, unit: AlertSuppressionDurationUnit): void { + expect( + within(screen.getByTestId('alertSuppressionDuration')).getByTestId('interval') + ).toHaveValue(value); + expect( + within(screen.getByTestId('alertSuppressionDuration')).getByTestId('timeType') + ).toHaveValue(unit); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/index.tsx similarity index 68% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/index.tsx rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/index.tsx index 99222756bcf2..b203cdea8f73 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/index.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import { EuiFieldNumber, EuiFormRow, EuiSelect, transparentize } from '@elastic/eui'; -import React, { useCallback } from 'react'; -import styled from 'styled-components'; - +import React, { memo, useCallback } from 'react'; +import { css } from '@emotion/css'; +import { EuiFieldNumber, EuiFormRow, EuiSelect, transparentize, useEuiTheme } from '@elastic/eui'; import type { FieldHook } from '../../../../shared_imports'; import { getFieldValidityAndErrorMessage } from '../../../../shared_imports'; import * as I18n from './translations'; @@ -21,39 +20,10 @@ interface DurationInputProps { durationUnitOptions?: Array<{ value: 's' | 'm' | 'h' | 'd'; text: string }>; } -const getNumberFromUserInput = (input: string, minimumValue = 0): number | undefined => { - const number = parseInt(input, 10); - if (Number.isNaN(number)) { - return minimumValue; - } else { - return Math.max(minimumValue, Math.min(number, Number.MAX_SAFE_INTEGER)); - } -}; - -const StyledEuiFormRow = styled(EuiFormRow)` - max-width: 235px; - - .euiFormControlLayout__append { - padding-inline: 0 !important; - } - - .euiFormControlLayoutIcons { - color: ${({ theme }) => theme.eui.euiColorPrimary}; - } -`; - -const MyEuiSelect = styled(EuiSelect)` - min-width: 106px; // Preserve layout when disabled & dropdown arrow is not rendered - box-shadow: none; - background: ${({ theme }) => - transparentize(theme.eui.euiColorPrimary, 0.1)} !important; // Override focus states etc. - color: ${({ theme }) => theme.eui.euiColorPrimary}; -`; - // This component is similar to the ScheduleItem component, but instead of combining the value // and unit into a single string it keeps them separate. This makes the component simpler and // allows for easier validation of values and units in APIs as well. -const DurationInputComponent: React.FC = ({ +export const DurationInput = memo(function DurationInputComponent({ durationValueField, durationUnitField, minimumValue = 0, @@ -64,7 +34,8 @@ const DurationInputComponent: React.FC = ({ { value: 'h', text: I18n.HOURS }, ], ...props -}: DurationInputProps) => { +}: DurationInputProps): JSX.Element { + const { euiTheme } = useEuiTheme(); const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(durationValueField); const { value: durationValue, setValue: setDurationValue } = durationValueField; const { value: durationUnit, setValue: setDurationUnit } = durationUnitField; @@ -84,17 +55,40 @@ const DurationInputComponent: React.FC = ({ [minimumValue, setDurationValue] ); + const durationFormRowStyle = css` + max-width: 235px; + + .euiFormControlLayout__append { + padding-inline: 0 !important; + } + + .euiFormControlLayoutIcons { + color: ${euiTheme.colors.primary}; + } + `; + const durationUnitSelectStyle = css` + min-width: 106px; // Preserve layout when disabled & dropdown arrow is not rendered + box-shadow: none; + background: ${transparentize( + euiTheme.colors.primary, + 0.1 + )} !important; // Override focus states etc. + color: ${euiTheme.colors.primary}; + `; + // EUI missing some props const rest = { disabled: isDisabled, ...props }; return ( - + @@ -106,8 +100,16 @@ const DurationInputComponent: React.FC = ({ data-test-subj="interval" {...rest} /> - + ); -}; +}); + +function getNumberFromUserInput(input: string, minimumValue = 0): number | undefined { + const number = parseInt(input, 10); -export const DurationInput = React.memo(DurationInputComponent); + if (Number.isNaN(number)) { + return minimumValue; + } else { + return Math.max(minimumValue, Math.min(number, Number.MAX_SAFE_INTEGER)); + } +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/translations.ts similarity index 82% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/translations.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/translations.ts index c460d2f7198b..51d659210c52 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/duration_input/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/duration_input/translations.ts @@ -34,3 +34,10 @@ export const DAYS = i18n.translate( defaultMessage: 'Days', } ); + +export const DURATION_UNIT_SELECTOR = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepScheduleRuleForm.durationUnitSelector', + { + defaultMessage: 'Duration unit selector', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations.test.tsx index 960df4c7de5b..31e139a335be 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations.test.tsx @@ -6,19 +6,24 @@ */ import React from 'react'; -import { - screen, - render, - act, - fireEvent, - waitFor, - waitForElementToBeRemoved, -} from '@testing-library/react'; +import { screen, render, act, fireEvent, waitFor } from '@testing-library/react'; import type { RelatedIntegration } from '../../../../../common/api/detection_engine'; import { FIELD_TYPES, Form, useForm } from '../../../../shared_imports'; import { createReactQueryWrapper } from '../../../../common/mock'; import { fleetIntegrationsApi } from '../../../fleet_integrations/api/__mocks__'; import { RelatedIntegrations } from './related_integrations'; +import { + clearEuiComboBoxSelection, + selectEuiComboBoxOption, + selectFirstEuiComboBoxOption, + showEuiComboBoxOptions, +} from '../../../../common/test/eui/combobox'; +import { + addRelatedIntegrationRow, + removeLastRelatedIntegrationRow, + setVersion, + waitForIntegrationsToBeLoaded, +} from './test_helpers'; // must match to the import in rules/related_integrations/use_integrations.tsx jest.mock('../../../fleet_integrations/api'); @@ -41,7 +46,6 @@ const COMBO_BOX_TOGGLE_BUTTON_TEST_ID = 'comboBoxToggleListButton'; const COMBO_BOX_SELECTION_TEST_ID = 'euiComboBoxPill'; const COMBO_BOX_CLEAR_BUTTON_TEST_ID = 'comboBoxClearButton'; const VERSION_INPUT_TEST_ID = 'relatedIntegrationVersionDependency'; -const REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID = 'relatedIntegrationRemove'; describe('RelatedIntegrations form part', () => { beforeEach(() => { @@ -708,72 +712,6 @@ function TestForm({ initialState, onSubmit }: TestFormProps): JSX.Element { ); } -function waitForIntegrationsToBeLoaded(): Promise { - return waitForElementToBeRemoved(screen.queryAllByRole('progressbar')); -} - -function addRelatedIntegrationRow(): Promise { - return act(async () => { - fireEvent.click(screen.getByText('Add integration')); - }); -} - -function removeLastRelatedIntegrationRow(): Promise { - return act(async () => { - const lastRemoveButton = screen.getAllByTestId(REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID).at(-1); - - if (!lastRemoveButton) { - throw new Error(`There are no "${REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID}" found`); - } - - fireEvent.click(lastRemoveButton); - }); -} - -function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { - fireEvent.click(comboBoxToggleButton); - - return waitFor(() => { - expect(screen.getByRole('listbox')).toBeInTheDocument(); - }); -} - -function selectEuiComboBoxOption({ - comboBoxToggleButton, - optionIndex, -}: { - comboBoxToggleButton: HTMLElement; - optionIndex: number; -}): Promise { - return act(async () => { - await showEuiComboBoxOptions(comboBoxToggleButton); - - fireEvent.click(screen.getAllByRole('option')[optionIndex]); - }); -} - -function clearEuiComboBoxSelection({ clearButton }: { clearButton: HTMLElement }): Promise { - return act(async () => { - fireEvent.click(clearButton); - }); -} - -function selectFirstEuiComboBoxOption({ - comboBoxToggleButton, -}: { - comboBoxToggleButton: HTMLElement; -}): Promise { - return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); -} - -function setVersion({ input, value }: { input: HTMLInputElement; value: string }): Promise { - return act(async () => { - fireEvent.input(input, { - target: { value }, - }); - }); -} - function submitForm(): Promise { return act(async () => { fireEvent.click(screen.getByText('Submit')); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/test_helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/test_helpers.ts new file mode 100644 index 000000000000..b8c51fd594e1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/test_helpers.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line import/no-extraneous-dependencies +import { act, fireEvent, waitForElementToBeRemoved, screen } from '@testing-library/react'; + +const REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID = 'relatedIntegrationRemove'; + +export function waitForIntegrationsToBeLoaded(): Promise { + return waitForElementToBeRemoved(screen.queryAllByRole('progressbar')); +} + +export function addRelatedIntegrationRow(): Promise { + return act(async () => { + fireEvent.click(screen.getByText('Add integration')); + }); +} + +export function removeLastRelatedIntegrationRow(): Promise { + return act(async () => { + const lastRemoveButton = screen.getAllByTestId(REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID).at(-1); + + if (!lastRemoveButton) { + throw new Error(`There are no "${REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID}" found`); + } + + fireEvent.click(lastRemoveButton); + }); +} + +export function setVersion({ + input, + value, +}: { + input: HTMLInputElement; + value: string; +}): Promise { + return act(async () => { + fireEvent.input(input, { + target: { value }, + }); + }); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/fields.ts new file mode 100644 index 000000000000..4956a2555bc9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/fields.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const THRESHOLD_ALERT_SUPPRESSION_ENABLED = 'thresholdAlertSuppressionEnabled' as const; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/index.ts new file mode 100644 index 000000000000..67848fbd5e3b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './threshold_alert_suppression_edit'; +export * from './fields'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/threshold_alert_suppression_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/threshold_alert_suppression_edit.tsx new file mode 100644 index 000000000000..a832bff648e8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/threshold_alert_suppression_edit.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { EuiPanel, EuiToolTip } from '@elastic/eui'; +import { CheckBoxField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { UseField, useFormData } from '../../../../shared_imports'; +import { THRESHOLD_ALERT_SUPPRESSION_ENABLED } from './fields'; +import { SuppressionDurationSelector } from '../alert_suppression_edit'; +import * as i18n from './translations'; + +interface ThresholdAlertSuppressionEditProps { + suppressionFieldNames: string[] | undefined; + disabled?: boolean; + disabledText?: string; +} + +export const ThresholdAlertSuppressionEdit = memo(function ThresholdAlertSuppressionEdit({ + suppressionFieldNames, + disabled, + disabledText, +}: ThresholdAlertSuppressionEditProps): JSX.Element { + const [{ [THRESHOLD_ALERT_SUPPRESSION_ENABLED]: suppressionEnabled }] = useFormData({ + watch: THRESHOLD_ALERT_SUPPRESSION_ENABLED, + }); + const content = ( + <> + + + + + + ); + + return disabled && disabledText ? ( + + {content} + + ) : ( + content + ); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/translations.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/translations.tsx new file mode 100644 index 000000000000..25b7158610b3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/threshold_alert_suppression_edit/translations.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const enableSuppressionForFields = (fields: string[]) => ( + {fields.join(', ')} }} + /> +); + +export const SUPPRESS_ALERTS = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.thresholdAlertSuppression.enable', + { + defaultMessage: 'Suppress alerts', + } +); + +export const THRESHOLD_SUPPRESSION_PER_RULE_EXECUTION_WARNING = i18n.translate( + 'xpack.securitySolution.ruleManagement.ruleFields.thresholdAlertSuppression.perRuleExecutionWarning', + { + defaultMessage: 'Per rule execution option is not available for Threshold rule type', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_views.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_view_list_items.ts similarity index 81% rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_views.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_view_list_items.ts index 248729f1f46e..3d2ba5d1c372 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_views.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/__mocks__/use_data_view_list_items.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const useDataViews = jest.fn().mockReturnValue({ +export const useDataViewListItems = jest.fn().mockReturnValue({ data: [], isFetching: false, }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/data_view_selector_field.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/data_view_selector_field.test.tsx index 6cfdf060434b..8648ade5164e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/data_view_selector_field.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/data_view_selector_field/data_view_selector_field.test.tsx @@ -9,14 +9,14 @@ import React from 'react'; import { screen, render } from '@testing-library/react'; import { TestProviders, useFormFieldMock } from '../../../../common/mock'; import { DataViewSelectorField } from './data_view_selector_field'; -import { useDataViews } from './use_data_views'; +import { useDataViewListItems } from './use_data_view_list_items'; jest.mock('../../../../common/lib/kibana'); -jest.mock('./use_data_views'); +jest.mock('./use_data_view_list_items'); describe('data_view_selector', () => { it('renders correctly', () => { - (useDataViews as jest.Mock).mockReturnValue({ data: [], isFetching: false }); + (useDataViewListItems as jest.Mock).mockReturnValue({ data: [], isFetching: false }); render( { }); it('disables the combobox while data views are fetching', () => { - (useDataViews as jest.Mock).mockReturnValue({ data: [], isFetching: true }); + (useDataViewListItems as jest.Mock).mockReturnValue({ data: [], isFetching: true }); render( { title: 'logs-*', }, ]; - (useDataViews as jest.Mock).mockReturnValue({ data: dataViews, isFetching: false }); + (useDataViewListItems as jest.Mock).mockReturnValue({ data: dataViews, isFetching: false }); render( { title: 'logs-*', }, ]; - (useDataViews as jest.Mock).mockReturnValue({ data: dataViews, isFetching: false }); + (useDataViewListItems as jest.Mock).mockReturnValue({ data: dataViews, isFetching: false }); render( { }); it('displays warning on missing data view', () => { - (useDataViews as jest.Mock).mockReturnValue({ data: [], isFetching: false }); + (useDataViewListItems as jest.Mock).mockReturnValue({ data: [], isFetching: false }); render( { @@ -615,11 +615,11 @@ export const buildAlertSuppressionDescription = ( export const buildAlertSuppressionWindowDescription = ( label: string, value: Duration, - groupByRadioSelection: GroupByOptions, + alertSuppressionDuration: AlertSuppressionDurationType, ruleType: Type ): ListItems[] => { const description = - groupByRadioSelection === GroupByOptions.PerTimePeriod + alertSuppressionDuration === AlertSuppressionDurationType.PerTimePeriod ? `${value.value}${value.unit}` : i18n.ALERT_SUPPRESSION_PER_RULE_EXECUTION; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx index f5a7e3963435..de46d09065f4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.test.tsx @@ -30,6 +30,15 @@ import { schema } from '../step_about_rule/schema'; import type { ListItems } from './types'; import type { AboutStepRule } from '../../../../detections/pages/detection_engine/rules/types'; import { createLicenseServiceMock } from '../../../../../common/license/mocks'; +import { + ALERT_SUPPRESSION_DURATION_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME, + ALERT_SUPPRESSION_FIELDS_FIELD_NAME, + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, +} from '../../../rule_creation/components/alert_suppression_edit'; +import { THRESHOLD_ALERT_SUPPRESSION_ENABLED } from '../../../rule_creation/components/threshold_alert_suppression_edit'; jest.mock('../../../../common/lib/kibana'); @@ -575,25 +584,25 @@ describe('description_step', () => { describe('alert suppression', () => { const suppressionFields = { - groupByDuration: { - unit: 'm', - value: 50, + [ALERT_SUPPRESSION_DURATION_FIELD_NAME]: { + [ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME]: 50, + [ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME]: 'm', }, - groupByRadioSelection: 'per-time-period', - enableThresholdSuppression: true, - groupByFields: ['agent.name'], - suppressionMissingFields: 'suppress', + [ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME]: 'per-time-period', + [THRESHOLD_ALERT_SUPPRESSION_ENABLED]: true, + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: ['agent.name'], + [ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME]: 'suppress', }; - describe('groupByDuration', () => { + describe(ALERT_SUPPRESSION_DURATION_FIELD_NAME, () => { ['query', 'saved_query'].forEach((ruleType) => { - test(`should be empty if groupByFields empty for ${ruleType} rule`, () => { + test(`should be empty if ${ALERT_SUPPRESSION_FIELDS_FIELD_NAME} empty for ${ruleType} rule`, () => { const result: ListItems[] = getDescriptionItem( - 'groupByDuration', + ALERT_SUPPRESSION_DURATION_FIELD_NAME, 'label', { ruleType: 'query', ...suppressionFields, - groupByFields: [], + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: [], }, mockFilterManager, mockLicenseService @@ -604,7 +613,7 @@ describe('description_step', () => { test(`should return item for ${ruleType} rule`, () => { const result: ListItems[] = getDescriptionItem( - 'groupByDuration', + ALERT_SUPPRESSION_DURATION_FIELD_NAME, 'label', { ruleType: 'query', @@ -620,7 +629,7 @@ describe('description_step', () => { test('should return item for threshold rule', () => { const result: ListItems[] = getDescriptionItem( - 'groupByDuration', + ALERT_SUPPRESSION_DURATION_FIELD_NAME, 'label', { ruleType: 'threshold', @@ -633,14 +642,14 @@ describe('description_step', () => { expect(result[0].description).toBe('50m'); }); - test('should return item for threshold rule if groupByFields empty', () => { + test(`should return item for threshold rule if ${ALERT_SUPPRESSION_FIELDS_FIELD_NAME} empty`, () => { const result: ListItems[] = getDescriptionItem( - 'groupByDuration', + ALERT_SUPPRESSION_DURATION_FIELD_NAME, 'label', { ruleType: 'threshold', ...suppressionFields, - groupByFields: [], + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: [], }, mockFilterManager, mockLicenseService @@ -651,12 +660,12 @@ describe('description_step', () => { test('should be empty for threshold rule if suppression not enabled', () => { const result: ListItems[] = getDescriptionItem( - 'groupByDuration', + ALERT_SUPPRESSION_DURATION_FIELD_NAME, 'label', { ruleType: 'threshold', ...suppressionFields, - enableThresholdSuppression: false, + [THRESHOLD_ALERT_SUPPRESSION_ENABLED]: false, }, mockFilterManager, mockLicenseService @@ -666,10 +675,10 @@ describe('description_step', () => { }); }); - describe('groupByFields', () => { + describe(ALERT_SUPPRESSION_FIELDS_FIELD_NAME, () => { test(`should be empty if rule type is 'threshold'`, () => { const result: ListItems[] = getDescriptionItem( - 'groupByFields', + ALERT_SUPPRESSION_FIELDS_FIELD_NAME, 'label', { ruleType: 'threshold', @@ -685,7 +694,7 @@ describe('description_step', () => { ['query', 'saved_query'].forEach((ruleType) => { test(`should return item for ${ruleType} rule`, () => { const result: ListItems[] = getDescriptionItem( - 'groupByFields', + ALERT_SUPPRESSION_FIELDS_FIELD_NAME, 'label', { ruleType, @@ -699,10 +708,10 @@ describe('description_step', () => { }); }); - describe('suppressionMissingFields', () => { + describe(ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, () => { test(`should be empty if rule type is 'threshold'`, () => { const result: ListItems[] = getDescriptionItem( - 'suppressionMissingFields', + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, 'label', { ruleType: 'threshold', @@ -718,7 +727,7 @@ describe('description_step', () => { ['query', 'saved_query'].forEach((ruleType) => { test(`should return item for ${ruleType} rule`, () => { const result: ListItems[] = getDescriptionItem( - 'suppressionMissingFields', + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, 'label', { ruleType, @@ -730,14 +739,14 @@ describe('description_step', () => { expect(result[0].description).toContain('Suppress'); }); - test(`should be empty if groupByFields empty for ${ruleType} rule`, () => { + test(`should be empty if ${ALERT_SUPPRESSION_FIELDS_FIELD_NAME} empty for ${ruleType} rule`, () => { const result: ListItems[] = getDescriptionItem( - 'suppressionMissingFields', + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, 'label', { ruleType: 'query', ...suppressionFields, - groupByFields: [], + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: [], }, mockFilterManager, mockLicenseService diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx index 4676f065f4af..657f592fe47c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx @@ -65,6 +65,13 @@ import { isSuppressionRuleConfiguredWithGroupBy, isSuppressionRuleConfiguredWithDuration, } from '../../../../../common/detection_engine/utils'; +import { + ALERT_SUPPRESSION_DURATION_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, + ALERT_SUPPRESSION_FIELDS_FIELD_NAME, + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, +} from '../../../rule_creation/components/alert_suppression_edit'; +import { THRESHOLD_ALERT_SUPPRESSION_ENABLED } from '../../../rule_creation/components/threshold_alert_suppression_edit'; const DescriptionListContainer = styled(EuiDescriptionList)` max-width: 600px; @@ -217,7 +224,7 @@ export const getDescriptionItem = ( }); } else if (field === 'responseActions') { return []; - } else if (field === 'groupByFields') { + } else if (field === ALERT_SUPPRESSION_FIELDS_FIELD_NAME) { const ruleType: Type = get('ruleType', data); const ruleCanHaveGroupByFields = isSuppressionRuleConfiguredWithGroupBy(ruleType); @@ -226,9 +233,9 @@ export const getDescriptionItem = ( } const values: string[] = get(field, data); return buildAlertSuppressionDescription(label, values, ruleType); - } else if (field === 'groupByRadioSelection') { + } else if (field === ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME) { return []; - } else if (field === 'groupByDuration') { + } else if (field === ALERT_SUPPRESSION_DURATION_FIELD_NAME) { const ruleType: Type = get('ruleType', data); const ruleCanHaveDuration = isSuppressionRuleConfiguredWithDuration(ruleType); @@ -239,21 +246,21 @@ export const getDescriptionItem = ( // threshold rule has suppression duration without grouping fields, but suppression should be explicitly enabled by user // query rule have suppression duration only if group by fields selected const showDuration = isThresholdRule(ruleType) - ? get('enableThresholdSuppression', data) === true - : get('groupByFields', data).length > 0; + ? get(THRESHOLD_ALERT_SUPPRESSION_ENABLED, data) === true + : get(ALERT_SUPPRESSION_FIELDS_FIELD_NAME, data).length > 0; if (showDuration) { const value: Duration = get(field, data); return buildAlertSuppressionWindowDescription( label, value, - get('groupByRadioSelection', data), + get(ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, data), ruleType ); } else { return []; } - } else if (field === 'suppressionMissingFields') { + } else if (field === ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME) { const ruleType: Type = get('ruleType', data); const ruleCanHaveSuppressionMissingFields = isSuppressionRuleConfiguredWithMissingFields(ruleType); @@ -261,7 +268,7 @@ export const getDescriptionItem = ( if (!ruleCanHaveSuppressionMissingFields) { return []; } - if (get('groupByFields', data).length > 0) { + if (get(ALERT_SUPPRESSION_FIELDS_FIELD_NAME, data).length > 0) { const value = get(field, data); return buildAlertSuppressionMissingFieldsDescription(label, value, ruleType); } else { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/translations.ts index 27dfec9818eb..5c43b9181adc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/translations.ts @@ -182,8 +182,8 @@ export const BUILDING_BLOCK_DESCRIPTION = i18n.translate( } ); -export const GROUP_BY_LABEL = i18n.translate( - 'xpack.securitySolution.detectionEngine.ruleDescription.groupByFieldsLabel', +export const ALERT_SUPPRESSION_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.alertSuppressionFieldsLabel', { defaultMessage: 'Suppress alerts by', } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/multi_select_fields/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/multi_select_fields/index.tsx index d38af219fe85..8a27d2f66809 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/multi_select_fields/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/multi_select_fields/index.tsx @@ -6,11 +6,9 @@ */ import React, { useMemo } from 'react'; - -import { EuiToolTip } from '@elastic/eui'; import type { DataViewFieldBase } from '@kbn/es-query'; +import { ComboBoxField } from '@kbn/es-ui-shared-plugin/static/forms/components'; import type { FieldHook } from '../../../../shared_imports'; -import { Field } from '../../../../shared_imports'; import { FIELD_PLACEHOLDER } from './translations'; interface MultiSelectAutocompleteProps { @@ -18,7 +16,6 @@ interface MultiSelectAutocompleteProps { isDisabled: boolean; field: FieldHook; fullWidth?: boolean; - disabledText?: string; dataTestSubj?: string; } @@ -28,7 +25,6 @@ const fieldDescribedByIds = 'detectionEngineMultiSelectAutocompleteField'; export const MultiSelectAutocompleteComponent: React.FC = ({ browserFields, - disabledText, isDisabled, field, fullWidth = false, @@ -46,21 +42,15 @@ export const MultiSelectAutocompleteComponent: React.FC ); - return isDisabled ? ( - - {fieldComponent} - - ) : ( - fieldComponent - ); }; export const MultiSelectFieldsAutocomplete = React.memo(MultiSelectAutocompleteComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx index bdbc01ada58f..cc303731b26e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_about_rule/index.test.tsx @@ -23,7 +23,7 @@ import type { } from '../../../../detections/pages/detection_engine/rules/types'; import { DataSourceType, - GroupByOptions, + AlertSuppressionDurationType, } from '../../../../detections/pages/detection_engine/rules/types'; import { fillEmptySeverityMappings } from '../../../../detections/pages/detection_engine/rules/helpers'; import { TestProviders } from '../../../../common/mock'; @@ -36,6 +36,16 @@ import { import type { FormHook } from '../../../../shared_imports'; import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; import { useKibana } from '../../../../common/lib/kibana'; +import { + ALERT_SUPPRESSION_DURATION_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME, + ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME, + ALERT_SUPPRESSION_FIELDS_FIELD_NAME, + ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME, +} from '../../../rule_creation/components/alert_suppression_edit'; +import { THRESHOLD_ALERT_SUPPRESSION_ENABLED } from '../../../rule_creation/components/threshold_alert_suppression_edit'; +import { AlertSuppressionMissingFieldsStrategyEnum } from '../../../../../common/api/detection_engine'; jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/containers/source'); @@ -69,16 +79,17 @@ export const stepDefineStepMLRule: DefineStepRule = { timeline: { id: null, title: null }, eqlOptions: {}, dataSourceType: DataSourceType.IndexPatterns, - groupByFields: ['host.name'], - groupByRadioSelection: GroupByOptions.PerRuleExecution, - groupByDuration: { - unit: 'm', - value: 5, + [ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: ['host.name'], + [ALERT_SUPPRESSION_DURATION_TYPE_FIELD_NAME]: AlertSuppressionDurationType.PerRuleExecution, + [ALERT_SUPPRESSION_DURATION_FIELD_NAME]: { + [ALERT_SUPPRESSION_DURATION_VALUE_FIELD_NAME]: 5, + [ALERT_SUPPRESSION_DURATION_UNIT_FIELD_NAME]: 'm', }, + [ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME]: AlertSuppressionMissingFieldsStrategyEnum.suppress, + [THRESHOLD_ALERT_SUPPRESSION_ENABLED]: false, newTermsFields: ['host.ip'], historyWindowSize: '7d', shouldLoadQueryDynamically: false, - enableThresholdSuppression: false, }; describe('StepAboutRuleComponent', () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx index f1dcfc74e792..50264fffabfb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx @@ -9,7 +9,8 @@ import React, { useEffect, useState } from 'react'; import { screen, fireEvent, render, within, act, waitFor } from '@testing-library/react'; import type { Type as RuleType } from '@kbn/securitysolution-io-ts-alerting-types'; import type { DataViewBase } from '@kbn/es-query'; -import { StepDefineRule, aggregatableFields } from '.'; +import type { FieldSpec } from '@kbn/data-plugin/common'; +import { StepDefineRule } from '.'; import type { StepDefineRuleProps } from '.'; import { mockBrowserFields } from '../../../../common/containers/source/mock'; import { useRuleFromTimeline } from '../../../../detections/containers/detection_engine/rules/use_rule_from_timeline'; @@ -25,6 +26,22 @@ import { createIndexPatternField, getSelectToggleButtonForName, } from '../../../rule_creation/components/required_fields/required_fields.test'; +import { ALERT_SUPPRESSION_FIELDS_FIELD_NAME } from '../../../rule_creation/components/alert_suppression_edit'; +import { + expectDuration, + expectSuppressionFields, + setDuration, + setDurationType, + setSuppressionFields, +} from '../../../rule_creation/components/alert_suppression_edit/test_helpers'; +import { + selectEuiComboBoxOption, + selectFirstEuiComboBoxOption, +} from '../../../../common/test/eui/combobox'; +import { + addRelatedIntegrationRow, + setVersion, +} from '../../../rule_creation/components/related_integrations/test_helpers'; // Mocks integrations jest.mock('../../../fleet_integrations/api'); @@ -48,7 +65,13 @@ jest.mock('../ai_assistant', () => { }; }); -jest.mock('../data_view_selector_field/use_data_views'); +jest.mock('../data_view_selector_field/use_data_view_list_items'); + +jest.mock('../../../../common/hooks/use_license', () => ({ + useLicense: jest.fn().mockReturnValue({ + isAtLeast: jest.fn().mockReturnValue(true), + }), +})); const mockRedirectLegacyUrl = jest.fn(); const mockGetLegacyUrlConflict = jest.fn(); @@ -149,53 +172,6 @@ jest.mock('react-redux', () => { jest.mock('../../../../detections/containers/detection_engine/rules/use_rule_from_timeline'); -test('aggregatableFields', function () { - expect( - aggregatableFields([ - { - name: 'error.message', - type: 'string', - esTypes: ['text'], - searchable: true, - aggregatable: false, - readFromDocValues: false, - }, - ]) - ).toEqual([]); -}); - -test('aggregatableFields with aggregatable: true', function () { - expect( - aggregatableFields([ - { - name: 'error.message', - type: 'string', - esTypes: ['text'], - searchable: true, - aggregatable: false, - readFromDocValues: false, - }, - { - name: 'file.path', - type: 'string', - esTypes: ['keyword'], - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - ]) - ).toEqual([ - { - name: 'file.path', - type: 'string', - esTypes: ['keyword'], - searchable: true, - aggregatable: true, - readFromDocValues: false, - }, - ]); -}); - const mockUseRuleFromTimeline = useRuleFromTimeline as jest.Mock; const onOpenTimeline = jest.fn(); @@ -218,6 +194,62 @@ describe.skip('StepDefineRule', () => { expect(screen.getByTestId('stepDefineRule')).toBeDefined(); }); + describe('alert suppression', () => { + it('persists state when switching between custom query and threshold rule types', async () => { + const mockFields: FieldSpec[] = [ + { + name: 'test-field', + type: 'string', + searchable: false, + aggregatable: true, + }, + ]; + + const { rerender } = render( + , + { + wrapper: TestProviders, + } + ); + + await setSuppressionFields(['test-field']); + setDurationType('Per time period'); + setDuration(10, 'h'); + + // switch to threshold rule type + rerender( + + ); + + expectDuration(10, 'h'); + + // switch back to custom query rule type + rerender( + + ); + + expectSuppressionFields(['test-field']); + expectDuration(10, 'h'); + }); + }); + describe('related integrations', () => { beforeEach(() => { fleetIntegrationsApi.fetchAllIntegrations.mockResolvedValue({ @@ -631,13 +663,12 @@ function TestForm({ ruleType={ruleType} index={stepDefineDefaultValue.index} threatIndex={stepDefineDefaultValue.threatIndex} - groupByFields={stepDefineDefaultValue.groupByFields} + alertSuppressionFields={stepDefineDefaultValue[ALERT_SUPPRESSION_FIELDS_FIELD_NAME]} dataSourceType={stepDefineDefaultValue.dataSourceType} shouldLoadQueryDynamically={stepDefineDefaultValue.shouldLoadQueryDynamically} queryBarTitle="" queryBarSavedId="" thresholdFields={[]} - enableThresholdSuppression={false} {...formProps} />