@cccsaurora/howler-ui 2.15.0-dev.283 → 2.15.0-dev.288

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ import type { HowlerExplainSearchRequest, HowlerExplainSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
+ export declare const uri: () => string;
3
+ export declare const post: (request?: HowlerExplainSearchRequest) => Promise<HowlerExplainSearchResponse>;
@@ -0,0 +1,8 @@
1
+ import { hpost, joinAllUri } from '@cccsaurora/howler-ui/api';
2
+ import { uri as parentUri } from '@cccsaurora/howler-ui/api/search';
3
+ export const uri = () => {
4
+ return joinAllUri(parentUri(), 'hit', 'explain');
5
+ };
6
+ export const post = (request) => {
7
+ return hpost(uri(), { ...(request || {}), eql_query: request?.query || 'howler.id:*' });
8
+ };
@@ -1,7 +1,8 @@
1
1
  import type { HowlerSearchRequest, HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
2
2
  import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
3
3
  import * as eql from '@cccsaurora/howler-ui/api/search/eql/hit';
4
+ import * as explain from '@cccsaurora/howler-ui/api/search/explain/hit';
4
5
  import * as sigma from '@cccsaurora/howler-ui/api/search/sigma/hit';
5
6
  export declare const uri: () => string;
6
7
  export declare const post: (request?: HowlerSearchRequest) => Promise<HowlerSearchResponse<Hit>>;
7
- export { eql, sigma };
8
+ export { eql, explain, sigma };
package/api/search/hit.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { hpost, joinUri } from '@cccsaurora/howler-ui/api';
2
2
  import { uri as parentUri } from '@cccsaurora/howler-ui/api/search';
3
3
  import * as eql from '@cccsaurora/howler-ui/api/search/eql/hit';
4
+ import * as explain from '@cccsaurora/howler-ui/api/search/explain/hit';
4
5
  import * as sigma from '@cccsaurora/howler-ui/api/search/sigma/hit';
5
6
  export const uri = () => {
6
7
  return joinUri(parentUri(), 'hit');
@@ -8,4 +9,4 @@ export const uri = () => {
8
9
  export const post = (request) => {
9
10
  return hpost(uri(), { ...(request || {}), query: request?.query || 'howler.id:*' });
10
11
  };
11
- export { eql, sigma };
12
+ export { eql, explain, sigma };
@@ -50,4 +50,14 @@ export type HowlerSigmaSearchRequest = {
50
50
  timeout?: number;
51
51
  filters?: string[];
52
52
  };
53
+ export type HowlerExplainSearchRequest = {
54
+ query: string;
55
+ };
56
+ export type HowlerExplainSearchResponse = {
57
+ valid: boolean;
58
+ explanations: {
59
+ valid: boolean;
60
+ explanation: string;
61
+ }[];
62
+ };
53
63
  export { action, analytic, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
@@ -62,7 +62,12 @@ falsepositives:
62
62
  level: informational
63
63
  `
64
64
  };
65
- const LUCENE_QUERY_OPTIONS = ['default', 'facet', 'groupby'];
65
+ const LUCENE_QUERY_OPTIONS = [
66
+ 'default',
67
+ 'facet',
68
+ 'groupby',
69
+ 'explain'
70
+ ];
66
71
  const CustomPopper = (props) => {
67
72
  return _jsx(Popper, { ...props, style: { width: 'fit-content' }, placement: "bottom-start" });
68
73
  };
@@ -110,6 +115,11 @@ const QueryBuilder = () => {
110
115
  ...searchProperties
111
116
  });
112
117
  }
118
+ else if (queryType === 'explain') {
119
+ result = await api.search.hit.explain.post({
120
+ query: sanitizeMultilineLucene(query)
121
+ });
122
+ }
113
123
  else {
114
124
  result = await api.search.hit.post({
115
125
  query: sanitizeMultilineLucene(query),
@@ -218,12 +228,12 @@ const QueryBuilder = () => {
218
228
  display: 'flex',
219
229
  alignSelf: 'stretch'
220
230
  }
221
- }, children: [smallButtons ? (_jsx(Tooltip, { title: t('route.actions.execute'), children: _jsx(IconButton, { size: "small", sx: { alignSelf: 'start' }, color: "success", onClick: execute, disabled: searchDisabled, children: loading ? (_jsx(CircularProgress, { size: 18, color: "success" })) : (_jsx(PlayArrowOutlined, { color: searchDisabled ? 'disabled' : 'success' })) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", startIcon: loading ? (_jsx(CircularProgress, { size: 18, color: "success" })) : (_jsx(PlayArrowOutlined, { color: searchDisabled ? 'disabled' : 'success', sx: { '& path': { stroke: 'currentcolor', strokeWidth: '1px' } } })), color: "success", onClick: execute, disabled: searchDisabled, children: t('route.actions.execute') })), _jsxs(Stack, { direction: compactLayout ? 'column' : 'row', spacing: 1, children: [_jsx(Autocomplete, { value: type, onChange: (__, value) => setType(value), options: QUERY_TYPES, getOptionLabel: option => t(`route.advanced.query.${option}`), renderOption: (props, option) => (_jsx(ListItemText, { ...props, sx: { flexDirection: 'column', alignItems: 'start !important' }, primary: t(`route.advanced.query.${option}`), secondary: t(`route.advanced.query.${option}.description`) })), renderInput: params => (_jsx(TextField, { ...params, size: "small", label: t('route.advanced.type'), sx: { minWidth: '250px' } })), sx: [
231
+ }, children: [smallButtons ? (_jsx(Tooltip, { title: t('route.actions.execute'), children: _jsx(IconButton, { size: "small", sx: { alignSelf: 'start' }, color: "success", onClick: execute, disabled: searchDisabled, children: loading ? (_jsx(CircularProgress, { size: 18, color: "success" })) : (_jsx(PlayArrowOutlined, { color: searchDisabled ? 'disabled' : 'success' })) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", startIcon: loading ? (_jsx(CircularProgress, { size: 18, color: "success" })) : (_jsx(PlayArrowOutlined, { color: searchDisabled ? 'disabled' : 'success', sx: { '& path': { stroke: 'currentcolor', strokeWidth: '1px' } } })), color: "success", onClick: execute, disabled: searchDisabled, children: t('route.actions.execute') })), _jsxs(Stack, { direction: compactLayout ? 'column' : 'row', spacing: 1, children: [_jsx(Autocomplete, { value: type, onChange: (__, value) => setType(value), options: QUERY_TYPES, getOptionLabel: option => t(`route.advanced.query.${option}`), renderOption: (props, option) => (_jsx(ListItemText, { ...props, sx: { flexDirection: 'column', alignItems: 'start !important' }, primary: t(`route.advanced.query.${option}`), secondary: t(`route.advanced.query.${option}.description`) })), renderInput: params => (_jsx(TextField, { ...params, size: "small", label: t('route.advanced.query.type'), sx: { minWidth: '250px' } })), sx: [
222
232
  !compactLayout && {
223
233
  height: '100%',
224
234
  '& .MuiFormControl-root': { height: '100%', '& > div': { height: '100%' } }
225
235
  }
226
- ], slotProps: { paper: { sx: { minWidth: '600px' } } } }), _jsx(Card, { variant: "outlined", sx: { flex: 1, maxWidth: '350px', minWidth: '210px' }, children: _jsxs(Stack, { spacing: 0.5, sx: { px: 1, alignItems: 'start' }, children: [_jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { whiteSpace: 'nowrap' }, children: [t('route.advanced.rows'), ": ", STEPS[rows]] }), _jsx(Slider, { size: "small", valueLabelDisplay: "off", value: rows, onChange: (_, value) => setRows(value), min: 0, max: 9, step: 1, marks: true, track: false, sx: { py: 0.5 } })] }) })] }), type === 'lucene' && (_jsx(Autocomplete, { size: "small", getOptionLabel: opt => t(`route.advanced.query.type.${opt}`), options: LUCENE_QUERY_OPTIONS, value: queryType, onChange: (_event, value) => setQueryType(value), renderInput: params => (_jsx(TextField, { ...params, label: t('route.advanced.query.type'), sx: { minWidth: '200px' } })) })), queryType === 'groupby' && (_jsx(Autocomplete, { size: "small", options: fieldOptions, value: groupByField, onChange: (__, value) => setGroupByField(value), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.pivot.field') }), sx: { minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), allFields && queryType !== 'facet' ? (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: allFields, onChange: (__, checked) => setAllFields(checked) }), label: t('route.advanced.fields.all'), sx: { '& > span': { color: 'text.secondary' }, alignSelf: 'start' } })) : (_jsx(Autocomplete, { fullWidth: true, renderTags: values => values.length <= 3 ? (_jsx(Stack, { direction: "row", spacing: 0.5, children: values.map(_value => (_jsx(Chip, { size: "small", label: _value }, _value))) })) : (_jsx(Tooltip, { title: _jsx(Stack, { spacing: 1, children: values.map(_value => (_jsx("span", { children: _value }, _value))) }), children: _jsx(Chip, { size: "small", label: values.length }) })), multiple: true, size: "small", options: fieldOptions, value: fields, onChange: (__, values) => (values.length > 0 ? setFields(values) : setAllFields(true)), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.fields') }), sx: { maxWidth: '500px', width: '20vw', minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), _jsx(FlexOne, {}), type === 'lucene' &&
236
+ ], slotProps: { paper: { sx: { minWidth: '600px' } } } }), _jsx(Card, { variant: "outlined", sx: { flex: 1, maxWidth: '350px', minWidth: '210px' }, children: _jsxs(Stack, { spacing: 0.5, sx: { px: 1, alignItems: 'start' }, children: [_jsxs(Typography, { variant: "caption", color: "text.secondary", sx: { whiteSpace: 'nowrap' }, children: [t('route.advanced.rows'), ": ", STEPS[rows]] }), _jsx(Slider, { size: "small", valueLabelDisplay: "off", value: rows, onChange: (_, value) => setRows(value), min: 0, max: 9, step: 1, marks: true, track: false, sx: { py: 0.5 } })] }) })] }), type === 'lucene' && (_jsx(Autocomplete, { size: "small", getOptionLabel: opt => t(`route.advanced.query.type.${opt}`), options: LUCENE_QUERY_OPTIONS, value: queryType, onChange: (_event, value) => setQueryType(value), renderInput: params => (_jsx(TextField, { ...params, label: t('route.advanced.query.lucene.type'), sx: { minWidth: '230px' } })), renderOption: (props, option) => (_jsx(ListItemText, { ...props, sx: { flexDirection: 'column', alignItems: 'start !important' }, primary: t(`route.advanced.query.type.${option}`), secondary: t(`route.advanced.query.type.${option}.description`) })) })), queryType === 'groupby' && (_jsx(Autocomplete, { size: "small", options: fieldOptions, value: groupByField, onChange: (__, value) => setGroupByField(value), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.pivot.field') }), sx: { minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), allFields && queryType !== 'facet' ? (_jsx(FormControlLabel, { control: _jsx(Checkbox, { size: "small", checked: allFields, onChange: (__, checked) => setAllFields(checked) }), label: t('route.advanced.fields.all'), sx: { '& > span': { color: 'text.secondary' }, alignSelf: 'start' } })) : (_jsx(Autocomplete, { fullWidth: true, renderTags: values => values.length <= 3 ? (_jsx(Stack, { direction: "row", spacing: 0.5, children: values.map(_value => (_jsx(Chip, { size: "small", label: _value }, _value))) })) : (_jsx(Tooltip, { title: _jsx(Stack, { spacing: 1, children: values.map(_value => (_jsx("span", { children: _value }, _value))) }), children: _jsx(Chip, { size: "small", label: values.length }) })), multiple: true, size: "small", options: fieldOptions, value: fields, onChange: (__, values) => (values.length > 0 ? setFields(values) : setAllFields(true)), renderInput: params => _jsx(TextField, { ...params, label: t('route.advanced.fields') }), sx: { maxWidth: '500px', width: '20vw', minWidth: '200px', '& label': { zIndex: 1200 } }, onKeyDown: onKeyDown, PopperComponent: CustomPopper })), _jsx(FlexOne, {}), type === 'lucene' &&
227
237
  (smallButtons ? (_jsx(Tooltip, { title: t('route.advanced.open'), children: _jsx(IconButton, { color: "primary", sx: { alignSelf: 'center' }, component: Link, disabled: !response, to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}`, children: _jsx(OpenInNew, { fontSize: "small" }) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", startIcon: _jsx(OpenInNew, {}), component: Link, disabled: !response, ...{ to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}` }, children: t('route.advanced.open') }))), smallButtons ? (_jsx(Tooltip, { title: response ? t('route.advanced.create.rule') : t('route.advanced.create.rule.disabled'), children: _jsx(IconButton, { size: "small", sx: { alignSelf: 'center' }, color: "info", onClick: onCreateRule, disabled: !response, children: _jsx(SsidChart, {}) }) })) : (_jsx(CustomButton, { size: "small", variant: "outlined", color: "info", startIcon: _jsx(SsidChart, {}), onClick: onCreateRule, disabled: !response, ...{ to: `/hits?query=${sanitizeMultilineLucene(query).replaceAll('\n', ' ').trim()}` }, tooltip: !response && t('route.advanced.create.rule.disabled'), children: t('route.advanced.create.rule') }))] }), _jsxs(Box, { width: "100%", height: "calc(100vh - 112px)", sx: { position: 'relative', overflow: 'hidden', borderTop: `thin solid ${theme.palette.divider}` }, children: [_jsx(Box, { sx: {
228
238
  position: 'absolute',
229
239
  top: 0,
@@ -436,8 +436,14 @@
436
436
  "route.advanced.pivot.field": "Group By Field",
437
437
  "route.advanced.query.type": "Query Type",
438
438
  "route.advanced.query.type.default": "Default",
439
+ "route.advanced.query.type.default.description": "Exceute a regular Howler search query in Lucene Syntax.",
439
440
  "route.advanced.query.type.facet": "Facet",
441
+ "route.advanced.query.type.facet.description": "Provides the counts of each value for a field. When faceting on an array field, each unique value will be included in the response. Each value is only counted once.",
440
442
  "route.advanced.query.type.groupby": "Group By",
443
+ "route.advanced.query.type.groupby.description": "Grouped results based on shared fields.",
444
+ "route.advanced.query.type.explain": "Explain",
445
+ "route.advanced.query.type.explain.description": "Get the elasticsearch explanation (i.e. the raw query executed against the cluster) of a given lucene query. Useful for debugging buggy lucene queries.",
446
+ "route.advanced.query.lucene.type": "Query Method",
441
447
  "route.analytics": "Analytics",
442
448
  "route.analytics.deleted": "Deleted Rule!",
443
449
  "route.analytics.detections": "Detections:",
@@ -99,6 +99,7 @@
99
99
  "date.select.end": "Sélectionner la date de fin",
100
100
  "date.search.range": "Plage de dates de recherche",
101
101
  "deprecated": "Obsolète",
102
+ "description": "Description",
102
103
  "divider": "OU",
103
104
  "direction": "Direction",
104
105
  "deprecation.instructions": "Instructions de mise à niveau",
@@ -260,6 +261,7 @@
260
261
  "hit.viewer.data": "Données brutes",
261
262
  "hit.viewer.details": "Détails",
262
263
  "hit.viewer.json": "JSON",
264
+ "hit.viewer.overview": "Vue d'ensemble",
263
265
  "hit.view.overview": "Vue d'ensemble",
264
266
  "hit.viewer.worklog": "Journal de Bord",
265
267
  "hit.viewer.related": "Liens connexes",
@@ -424,6 +426,7 @@
424
426
  "route.advanced.rows": "Nombre de lignes à renvoyer",
425
427
  "route.advanced.create.rule": "Créer une règle",
426
428
  "route.advanced.create.rule.disabled": "Vous devez d'abord lancer votre recherche pour vous assurer qu'elle est valide",
429
+ "route.advanced.execute.query": "Exécuter la requête",
427
430
  "route.advanced.query.lucene": "Requête Lucene",
428
431
  "route.advanced.query.lucene.description": "Apache Lucence est une syntaxe de recherche utilisée par elasticsearch, Howler et apache lucene. Voir notre documentation pour plus d'informations à ce sujet.",
429
432
  "route.advanced.query.eql": "Requête EQL",
@@ -436,8 +439,14 @@
436
439
  "route.advanced.pivot.field": "Champ à regrouper par",
437
440
  "route.advanced.query.type": "Type de requête",
438
441
  "route.advanced.query.type.default": "Défaut",
442
+ "route.advanced.query.type.default.description": "Exécuter une requête de recherche Howler normale en syntaxe Lucene.",
439
443
  "route.advanced.query.type.facet": "Facette",
444
+ "route.advanced.query.type.facet.description": "Fournit les comptages de chaque valeur pour un champ. Lors du facettage sur un champ de tableau, chaque valeur unique sera incluse dans la réponse. Chaque valeur n'est comptée qu'une seule fois.",
440
445
  "route.advanced.query.type.groupby": "Regrouper",
446
+ "route.advanced.query.type.groupby.description": "Résultats groupés basés sur des champs partagés.",
447
+ "route.advanced.query.type.explain": "Expliquer",
448
+ "route.advanced.query.type.explain.description": "Obtenir l'explication elasticsearch (c'est-à-dire la requête brute exécutée contre le cluster) d'une requête lucene donnée. Utile pour déboguer des requêtes lucene défectueuses.",
449
+ "route.advanced.query.lucene.type": "Méthode de requête",
441
450
  "route.analytics": "Analyses",
442
451
  "route.analytics.deleted": "Règle supprimée!",
443
452
  "route.analytics.detections": "Détections:",
package/package.json CHANGED
@@ -96,7 +96,7 @@
96
96
  "internal-slot": "1.0.7"
97
97
  },
98
98
  "type": "module",
99
- "version": "2.15.0-dev.283",
99
+ "version": "2.15.0-dev.288",
100
100
  "exports": {
101
101
  "./i18n": "./i18n.js",
102
102
  "./index.css": "./index.css",
@@ -145,6 +145,7 @@
145
145
  "./api/hit/comments": "./api/hit/comments/index.js",
146
146
  "./api/search/facet/*": "./api/search/facet/*.js",
147
147
  "./api/search/facet": "./api/search/facet/index.js",
148
+ "./api/search/explain/*": "./api/search/explain/*.js",
148
149
  "./api/search/count/*": "./api/search/count/*.js",
149
150
  "./api/search/count": "./api/search/count/index.js",
150
151
  "./api/search/fields/*": "./api/search/fields/*.js",