@cccsaurora/howler-ui 2.19.0-dev.989 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/action/index.d.ts +5 -4
- package/api/action/index.js +8 -8
- package/api/analytic/index.d.ts +5 -5
- package/api/analytic/index.js +5 -6
- package/api/analytic/owner.d.ts +2 -1
- package/api/analytic/owner.js +2 -2
- package/api/dossier/index.d.ts +4 -3
- package/api/dossier/index.js +6 -6
- package/api/hit/index.d.ts +3 -2
- package/api/hit/index.js +4 -4
- package/api/hit/labels.d.ts +3 -2
- package/api/hit/labels.js +4 -4
- package/api/hit/overwrite.d.ts +2 -1
- package/api/hit/overwrite.js +2 -2
- package/api/hit/transition.d.ts +2 -1
- package/api/hit/transition.js +2 -2
- package/api/index.d.ts +10 -5
- package/api/index.js +18 -10
- package/api/overview/index.d.ts +4 -3
- package/api/overview/index.js +6 -6
- package/api/search/action.js +1 -1
- package/api/search/analytic.js +1 -1
- package/api/search/case.d.ts +4 -0
- package/api/search/case.js +8 -0
- package/api/search/count/hit.js +1 -1
- package/api/search/dossier.js +1 -1
- package/api/search/eql/hit.js +1 -1
- package/api/search/explain/hit.js +1 -1
- package/api/search/facet/hit.d.ts +1 -3
- package/api/search/facet/hit.js +1 -1
- package/api/search/facet/index.d.ts +3 -1
- package/api/search/grouped/hit.js +1 -1
- package/api/search/grouped/user.js +1 -1
- package/api/search/histogram/hit.js +1 -1
- package/api/search/hit.js +1 -1
- package/api/search/index.d.ts +3 -1
- package/api/search/index.js +2 -1
- package/api/search/overview.d.ts +2 -2
- package/api/search/overview.js +1 -1
- package/api/search/sigma/hit.js +1 -1
- package/api/search/template.js +1 -1
- package/api/search/user.js +1 -1
- package/api/search/view.js +1 -1
- package/api/socket/index.d.ts +3 -0
- package/api/socket/index.js +6 -0
- package/api/socket/viewers.d.ts +2 -0
- package/api/socket/viewers.js +8 -0
- package/api/socket/viewers.test.js +44 -0
- package/api/template/index.d.ts +4 -3
- package/api/template/index.js +6 -6
- package/api/user/index.d.ts +2 -1
- package/api/user/index.js +2 -2
- package/api/v2/case/index.d.ts +9 -0
- package/api/v2/case/index.js +21 -0
- package/api/v2/case/items.d.ts +9 -0
- package/api/v2/case/items.js +19 -0
- package/api/v2/case/rules.d.ts +6 -0
- package/api/v2/case/rules.js +18 -0
- package/api/v2/fuzzy.d.ts +17 -0
- package/api/v2/fuzzy.js +11 -0
- package/api/v2/index.d.ts +5 -0
- package/api/v2/index.js +7 -0
- package/api/v2/search/facet.d.ts +3 -0
- package/api/v2/search/facet.js +12 -0
- package/api/v2/search/index.d.ts +6 -0
- package/api/v2/search/index.js +24 -0
- package/api/view/index.d.ts +4 -3
- package/api/view/index.js +6 -6
- package/branding/AppBrand.d.ts +2 -2
- package/commons/components/app/AppSkeleton.js +1 -1
- package/commons/components/app/hooks/useAppConfigs.d.ts +2 -2
- package/commons/components/app/hooks/useAppSitemap.d.ts +5 -2
- package/commons/components/breadcrumbs/BreadcrumbIcon.js +1 -1
- package/commons/components/breadcrumbs/Breadcrumbs.d.ts +3 -2
- package/commons/components/display/AppToc.d.ts +2 -1
- package/commons/components/display/AppToc.js +1 -1
- package/commons/components/display/hooks/useAppColor.d.ts +1 -1
- package/commons/components/leftnav/LeftNavDrawer.d.ts +2 -1
- package/commons/components/leftnav/LeftNavDrawer.js +1 -1
- package/commons/components/leftnav/LeftNavGroup.d.ts +2 -1
- package/commons/components/leftnav/LeftNavItem.d.ts +2 -1
- package/commons/components/leftnav/LeftNavItem.js +1 -1
- package/commons/components/notification/Notification.js +1 -1
- package/commons/components/notification/elements/NotificationHeader.js +1 -1
- package/commons/components/notification/elements/NotificationItems.js +1 -1
- package/commons/components/notification/elements/item/NotificationItemAuthor.js +1 -1
- package/commons/components/pages/PageCenter.d.ts +2 -1
- package/commons/components/pages/PageContent.d.ts +2 -1
- package/commons/components/pages/PageFullScreen.d.ts +2 -1
- package/commons/components/search/AppSearch.js +1 -1
- package/commons/components/search/AppSearchInput.d.ts +2 -1
- package/commons/components/search/AppSearchResult.d.ts +2 -1
- package/commons/components/topnav/AppBar.d.ts +2 -1
- package/commons/components/topnav/AppName.d.ts +3 -2
- package/commons/components/topnav/AppSwitcher.d.ts +2 -1
- package/commons/components/topnav/ThemeSelection.d.ts +2 -1
- package/commons/components/topnav/ThemeSelection.js +1 -1
- package/commons/components/topnav/UserProfile.d.ts +2 -1
- package/commons/components/topnav/UserProfile.js +1 -1
- package/commons/components/utils/hooks/useThemeBuilder.d.ts +1 -1
- package/components/app/App.js +52 -12
- package/components/app/drawers/ApiKeyDrawer.js +1 -1
- package/components/app/hooks/useMatchers.d.ts +1 -1
- package/components/app/hooks/useMatchers.js +27 -15
- package/components/app/hooks/useMatchers.test.js +22 -22
- package/components/app/hooks/useTitle.js +6 -6
- package/components/app/providers/AnalyticProvider.js +1 -1
- package/components/app/providers/AvatarProvider.js +1 -1
- package/components/app/providers/FavouritesProvider.js +3 -3
- package/components/app/providers/GridColumnsProvider.d.ts +52 -0
- package/components/app/providers/GridColumnsProvider.js +177 -0
- package/components/app/providers/GridColumnsProvider.test.js +255 -0
- package/components/app/providers/ModalProvider.d.ts +1 -0
- package/components/app/providers/OverviewProvider.js +1 -1
- package/components/app/providers/ParameterProvider.d.ts +25 -3
- package/components/app/providers/ParameterProvider.js +163 -226
- package/components/app/providers/ParameterProvider.test.js +358 -94
- package/components/app/providers/RecordProvider.d.ts +23 -0
- package/components/app/providers/{HitProvider.js → RecordProvider.js} +47 -43
- package/components/app/providers/{HitSearchProvider.d.ts → RecordSearchProvider.d.ts} +6 -6
- package/components/app/providers/{HitSearchProvider.js → RecordSearchProvider.js} +24 -37
- package/components/app/providers/{HitSearchProvider.test.js → RecordSearchProvider.test.js} +52 -71
- package/components/app/providers/SearchResponseProvider.d.ts +21 -0
- package/components/app/providers/SearchResponseProvider.js +81 -0
- package/components/app/providers/SearchResponseProvider.test.d.ts +1 -0
- package/components/app/providers/SearchResponseProvider.test.js +406 -0
- package/components/app/providers/SocketProvider.d.ts +11 -2
- package/components/app/providers/SocketProvider.js +21 -8
- package/components/app/providers/UserListProvider.js +28 -8
- package/components/app/providers/UserListProvider.test.d.ts +1 -0
- package/components/app/providers/UserListProvider.test.js +150 -0
- package/components/app/providers/ViewProvider.js +3 -3
- package/components/app/providers/ViewProvider.test.js +4 -1
- package/components/elements/Comment.js +2 -2
- package/components/elements/ContextMenu.d.ts +56 -0
- package/components/elements/ContextMenu.js +113 -0
- package/components/elements/ContextMenu.test.d.ts +1 -0
- package/components/elements/ContextMenu.test.js +215 -0
- package/components/elements/EditRow.js +4 -4
- package/components/{routes/overviews/OverviewEditor.js → elements/MarkdownEditor.js} +3 -3
- package/components/elements/ObjectDetails.d.ts +6 -0
- package/components/elements/{hit/HitDetails.js → ObjectDetails.js} +17 -17
- package/components/elements/PluginChip.d.ts +1 -1
- package/components/elements/PluginChip.js +0 -1
- package/components/elements/PluginTypography.d.ts +2 -1
- package/components/elements/PluginTypography.js +3 -2
- package/components/elements/UserList.d.ts +5 -2
- package/components/elements/UserList.js +34 -17
- package/components/elements/UserList.test.d.ts +1 -0
- package/components/elements/UserList.test.js +122 -0
- package/components/elements/addons/buttons/CustomIconButton.test.d.ts +1 -0
- package/components/elements/addons/buttons/CustomIconButton.test.js +81 -0
- package/components/elements/addons/buttons/index.d.ts +1 -1
- package/components/elements/addons/layout/FlexOne.d.ts +3 -2
- package/components/elements/addons/layout/FlexPort.d.ts +2 -1
- package/components/elements/addons/layout/FlexVertical.d.ts +2 -1
- package/components/elements/addons/layout/FlexVertical.test.d.ts +1 -0
- package/components/elements/addons/layout/FlexVertical.test.js +32 -0
- package/components/elements/addons/layout/vsbox/VSBoxContent.test.d.ts +1 -0
- package/components/elements/addons/layout/vsbox/VSBoxContent.test.js +26 -0
- package/components/elements/addons/layout/vsbox/VSBoxElement.d.ts +2 -1
- package/components/elements/addons/lists/TuiList.d.ts +2 -1
- package/components/elements/addons/lists/TuiListBase.d.ts +2 -1
- package/components/elements/addons/lists/TuiListElement.d.ts +3 -2
- package/components/elements/addons/lists/TuiListElement.js +0 -1
- package/components/elements/addons/lists/TuiListMenu.d.ts +2 -1
- package/components/elements/addons/lists/TuiListMenu.js +1 -1
- package/components/elements/addons/lists/table/TuiTableHeader.d.ts +3 -2
- package/components/elements/addons/lists/table/index.d.ts +1 -1
- package/components/elements/addons/search/SearchPagination.d.ts +2 -1
- package/components/elements/addons/search/SearchPagination.js +4 -4
- package/components/elements/addons/search/SearchTotal.js +1 -1
- package/components/elements/addons/search/SearchTotal.test.js +5 -3
- package/components/elements/addons/search/phrase/Phrase.js +1 -1
- package/components/elements/addons/search/phrase/WordLexer.test.d.ts +1 -0
- package/components/elements/addons/search/phrase/WordLexer.test.js +55 -0
- package/components/elements/case/CaseCard.d.ts +12 -0
- package/components/elements/case/CaseCard.js +43 -0
- package/components/elements/case/CasePreview.d.ts +6 -0
- package/components/elements/case/CasePreview.js +17 -0
- package/components/elements/case/StatusIcon.d.ts +5 -0
- package/components/elements/case/StatusIcon.js +13 -0
- package/components/elements/display/ActionButton.js +1 -1
- package/components/elements/display/ChipPopper.d.ts +1 -1
- package/components/elements/display/ChipPopper.js +7 -6
- package/components/elements/display/Classification.js +1 -1
- package/components/elements/display/ClassificationChip.d.ts +14 -0
- package/components/elements/display/ClassificationChip.js +36 -0
- package/components/elements/display/HandlebarsMarkdown.js +38 -3
- package/components/elements/display/HowlerAvatar.js +2 -2
- package/components/elements/display/HowlerCard.js +1 -1
- package/components/elements/display/HowlerCard.test.js +5 -9
- package/components/elements/display/Image.js +3 -3
- package/components/elements/display/ItemManager.d.ts +2 -2
- package/components/elements/display/ItemManager.js +3 -3
- package/components/elements/display/Markdown.js +2 -2
- package/components/elements/display/Modal.js +2 -0
- package/components/elements/display/QueryResultText.d.ts +1 -0
- package/components/elements/display/QueryResultText.js +2 -2
- package/components/elements/display/handlebars/helpers.d.ts +4 -0
- package/components/elements/display/handlebars/helpers.js +134 -35
- package/components/elements/display/json/JSONViewer.js +2 -2
- package/components/elements/display/modals/RationaleModal.js +2 -2
- package/components/elements/display/modals/RationaleModal.test.js +1 -1
- package/components/elements/event/EventCard.d.ts +6 -0
- package/components/elements/event/EventCard.js +22 -0
- package/components/elements/event/EventPreview.d.ts +6 -0
- package/components/elements/event/EventPreview.js +12 -0
- package/components/elements/hit/HitActions.d.ts +1 -1
- package/components/elements/hit/HitActions.js +5 -5
- package/components/elements/hit/HitBanner.d.ts +1 -0
- package/components/elements/hit/HitBanner.js +32 -50
- package/components/elements/hit/HitCard.d.ts +4 -0
- package/components/elements/hit/HitCard.js +7 -7
- package/components/elements/hit/HitLabels.js +3 -3
- package/components/elements/hit/HitLabels.test.d.ts +1 -0
- package/components/elements/hit/HitLabels.test.js +77 -0
- package/components/elements/hit/HitLinks.d.ts +5 -4
- package/components/elements/hit/HitLinks.js +28 -10
- package/components/elements/hit/HitNotebooks.js +32 -31
- package/components/elements/hit/HitOutline.d.ts +1 -0
- package/components/elements/hit/HitOutline.js +4 -4
- package/components/elements/hit/HitOverview.js +2 -2
- package/components/elements/hit/{HitQuickSearch.d.ts → HitPreview.d.ts} +3 -3
- package/components/elements/hit/{HitQuickSearch.js → HitPreview.js} +11 -5
- package/components/elements/hit/HitSummary.d.ts +2 -1
- package/components/elements/hit/HitSummary.js +8 -7
- package/components/elements/hit/NotebookTooltip.d.ts +3 -0
- package/components/elements/hit/NotebookTooltip.js +8 -0
- package/components/elements/hit/PivotTooltip.d.ts +7 -0
- package/components/elements/hit/PivotTooltip.js +9 -0
- package/components/elements/hit/RelatedLinkTooltip.d.ts +6 -0
- package/components/elements/hit/RelatedLinkTooltip.js +8 -0
- package/components/elements/hit/ResolvePivotUrl.d.ts +4 -0
- package/components/elements/hit/ResolvePivotUrl.js +22 -0
- package/components/elements/hit/actions/ButtonActions.js +1 -3
- package/components/elements/hit/aggregate/HitGraph.js +9 -9
- package/components/elements/hit/elements/AnalyticLink.d.ts +9 -0
- package/components/elements/hit/elements/AnalyticLink.js +22 -0
- package/components/elements/hit/elements/{HitUsers.d.ts → Assigned.d.ts} +2 -4
- package/components/elements/hit/elements/Assigned.js +35 -0
- package/components/elements/hit/elements/Assigned.test.d.ts +1 -0
- package/components/elements/hit/elements/Assigned.test.js +65 -0
- package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.js +6 -5
- package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.d.ts +2 -1
- package/components/{routes/hits/search → elements/hit}/grid/ColumnHeader.js +3 -3
- package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.d.ts +2 -1
- package/components/{routes/hits/search → elements/hit}/grid/EnhancedCell.js +2 -2
- package/components/{routes/hits/search/grid/HitRow.d.ts → elements/hit/grid/RecordRow.d.ts} +4 -3
- package/components/{routes/hits/search/grid/HitRow.js → elements/hit/grid/RecordRow.js} +12 -10
- package/components/elements/hit/grid/RecordTable.d.ts +13 -0
- package/components/elements/hit/grid/RecordTable.js +84 -0
- package/components/elements/hit/outlines/DefaultOutline.js +2 -4
- package/components/elements/hit/related/PivotLink.d.ts +3 -0
- package/components/elements/hit/related/PivotLink.js +6 -3
- package/components/elements/hit/related/RelatedLink.d.ts +1 -0
- package/components/elements/hit/related/RelatedLink.js +18 -15
- package/components/elements/hit/related/RelatedRecords.js +63 -0
- package/components/elements/{hit/HitComments.d.ts → record/RecordComments.d.ts} +5 -4
- package/components/elements/{hit/HitComments.js → record/RecordComments.js} +33 -34
- package/components/{routes/hits/search/HitContextMenu.d.ts → elements/record/RecordContextMenu.d.ts} +3 -3
- package/components/elements/record/RecordContextMenu.js +268 -0
- package/components/elements/record/RecordContextMenu.test.d.ts +1 -0
- package/components/{routes/hits/search/HitContextMenu.test.js → elements/record/RecordContextMenu.test.js} +143 -49
- package/components/elements/record/RecordRelated.d.ts +7 -0
- package/components/elements/record/RecordRelated.js +34 -0
- package/components/elements/{hit/HitWorklog.d.ts → record/RecordWorklog.d.ts} +4 -3
- package/components/elements/{hit/HitWorklog.js → record/RecordWorklog.js} +16 -14
- package/components/elements/search/FuzzySearchBar.d.ts +7 -0
- package/components/elements/search/FuzzySearchBar.js +33 -0
- package/components/elements/search/FuzzySearchBar.test.d.ts +1 -0
- package/components/elements/search/FuzzySearchBar.test.js +87 -0
- package/components/elements/view/LayoutToggle.d.ts +8 -0
- package/components/elements/view/LayoutToggle.js +12 -0
- package/components/elements/view/ViewTitle.d.ts +4 -0
- package/components/elements/view/ViewTitle.js +13 -3
- package/components/hooks/useHitActions.d.ts +1 -1
- package/components/hooks/useHitActions.js +5 -5
- package/components/hooks/useLocalStorage.js +1 -1
- package/components/hooks/useLocalStorageItem.test.js +2 -2
- package/components/hooks/useMyApi.d.ts +1 -1
- package/components/hooks/useMyChart.d.ts +23 -23
- package/components/hooks/useMyPreferences.js +10 -1
- package/components/hooks/useMySearch.js +2 -2
- package/components/hooks/useMySitemap.js +3 -1
- package/components/hooks/useMySnackbar.js +1 -1
- package/components/hooks/useMyTheme.js +9 -2
- package/components/hooks/useMyTheme.test.d.ts +1 -0
- package/components/hooks/useMyTheme.test.js +35 -0
- package/components/hooks/useMyUserFunctions.d.ts +21 -21
- package/components/hooks/{useHitSelection.d.ts → useRecordSelection.d.ts} +2 -2
- package/components/hooks/{useHitSelection.js → useRecordSelection.js} +12 -33
- package/components/hooks/useRelatedRecords.d.ts +13 -0
- package/components/hooks/useRelatedRecords.js +32 -0
- package/components/logins/Login.js +1 -1
- package/components/logins/auth/OAuthLogin.js +1 -1
- package/components/logins/hooks/useLogin.js +2 -2
- package/components/routes/ErrorBoundary.d.ts +2 -13
- package/components/routes/ErrorBoundary.js +12 -1
- package/components/routes/ErrorBoundary.test.js +27 -4
- package/components/routes/action/edit/ActionEditor.js +6 -6
- package/components/routes/action/shared/OperationEntry.js +1 -1
- package/components/routes/action/shared/OperationStep.js +1 -1
- package/components/routes/action/useMyActionFunctions.js +6 -3
- package/components/routes/action/view/ActionDetails.js +9 -3
- package/components/routes/action/view/ActionSearch.js +25 -19
- package/components/routes/admin/users/UserEditor.js +1 -1
- package/components/routes/admin/users/UserSearch.js +5 -5
- package/components/routes/advanced/QueryBuilder.js +6 -29
- package/components/routes/advanced/QueryEditor.js +3 -3
- package/components/routes/advanced/eqlTokenProvider.test.d.ts +1 -0
- package/components/routes/advanced/eqlTokenProvider.test.js +34 -0
- package/components/routes/advanced/historyCompletionProvider.js +3 -3
- package/components/routes/advanced/luceneCompletionProvider.js +1 -1
- package/components/routes/advanced/luceneTokenProvider.test.d.ts +1 -0
- package/components/routes/advanced/luceneTokenProvider.test.js +34 -0
- package/components/routes/analytics/AnalyticComments.js +3 -5
- package/components/routes/analytics/AnalyticDetails.js +8 -56
- package/components/routes/analytics/AnalyticHitComments.js +1 -1
- package/components/routes/analytics/AnalyticHitComments.test.js +1 -1
- package/components/routes/analytics/AnalyticOverview.js +2 -2
- package/components/routes/analytics/AnalyticOverviews.js +1 -1
- package/components/routes/analytics/AnalyticSearch.js +16 -25
- package/components/routes/analytics/AnalyticTemplates.js +1 -1
- package/components/routes/analytics/TriageSettings.js +2 -2
- package/components/routes/analytics/widgets/Assessment.js +1 -1
- package/components/routes/analytics/widgets/Created.js +1 -1
- package/components/routes/analytics/widgets/Escalation.js +1 -1
- package/components/routes/analytics/widgets/Stacked.js +1 -1
- package/components/routes/cases/CaseViewer.d.ts +2 -0
- package/components/routes/cases/CaseViewer.js +44 -0
- package/components/routes/cases/CaseViewer.test.d.ts +1 -0
- package/components/routes/cases/CaseViewer.test.js +156 -0
- package/components/routes/cases/Cases.d.ts +2 -0
- package/components/routes/cases/Cases.js +140 -0
- package/components/routes/cases/constants.d.ts +6 -0
- package/components/routes/cases/constants.js +6 -0
- package/components/routes/cases/detail/AlertPanel.d.ts +6 -0
- package/components/routes/cases/detail/AlertPanel.js +34 -0
- package/components/routes/cases/detail/AlertPanel.test.d.ts +1 -0
- package/components/routes/cases/detail/AlertPanel.test.js +107 -0
- package/components/routes/cases/detail/CaseDashboard.d.ts +7 -0
- package/components/routes/cases/detail/CaseDashboard.js +66 -0
- package/components/routes/cases/detail/CaseDashboard.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseDashboard.test.js +218 -0
- package/components/routes/cases/detail/CaseDetails.d.ts +6 -0
- package/components/routes/cases/detail/CaseDetails.js +79 -0
- package/components/routes/cases/detail/CaseDetails.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseDetails.test.js +145 -0
- package/components/routes/cases/detail/CaseObservables.d.ts +6 -0
- package/components/routes/cases/detail/CaseObservables.js +127 -0
- package/components/routes/cases/detail/CaseObservables.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseObservables.test.js +167 -0
- package/components/routes/cases/detail/CaseOverview.d.ts +7 -0
- package/components/routes/cases/detail/CaseOverview.js +40 -0
- package/components/routes/cases/detail/CaseRules.d.ts +7 -0
- package/components/routes/cases/detail/CaseRules.js +55 -0
- package/components/routes/cases/detail/CaseRules.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseRules.test.js +238 -0
- package/components/routes/cases/detail/CaseSearch.d.ts +3 -0
- package/components/routes/cases/detail/CaseSearch.js +78 -0
- package/components/routes/cases/detail/CaseSearch.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseSearch.test.js +154 -0
- package/components/routes/cases/detail/CaseSidebar.d.ts +8 -0
- package/components/routes/cases/detail/CaseSidebar.js +147 -0
- package/components/routes/cases/detail/CaseSidebar.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseSidebar.test.js +155 -0
- package/components/routes/cases/detail/CaseTask.d.ts +14 -0
- package/components/routes/cases/detail/CaseTask.js +71 -0
- package/components/routes/cases/detail/CaseTask.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseTask.test.js +143 -0
- package/components/routes/cases/detail/CaseTimeline.d.ts +12 -0
- package/components/routes/cases/detail/CaseTimeline.js +120 -0
- package/components/routes/cases/detail/CaseTimeline.test.d.ts +1 -0
- package/components/routes/cases/detail/CaseTimeline.test.js +319 -0
- package/components/routes/cases/detail/CreateRuleDialog.d.ts +9 -0
- package/components/routes/cases/detail/CreateRuleDialog.js +145 -0
- package/components/routes/cases/detail/CreateRuleDialog.test.d.ts +1 -0
- package/components/routes/cases/detail/CreateRuleDialog.test.js +218 -0
- package/components/routes/cases/detail/ItemPage.d.ts +6 -0
- package/components/routes/cases/detail/ItemPage.js +100 -0
- package/components/routes/cases/detail/ItemPage.test.d.ts +1 -0
- package/components/routes/cases/detail/ItemPage.test.js +238 -0
- package/components/routes/cases/detail/MarkdownPage.d.ts +8 -0
- package/components/routes/cases/detail/MarkdownPage.js +63 -0
- package/components/routes/cases/detail/MarkdownPage.test.d.ts +1 -0
- package/components/routes/cases/detail/MarkdownPage.test.js +116 -0
- package/components/routes/cases/detail/RelatedCasePanel.d.ts +6 -0
- package/components/routes/cases/detail/RelatedCasePanel.js +34 -0
- package/components/routes/cases/detail/RelatedCasePanel.test.d.ts +1 -0
- package/components/routes/cases/detail/RelatedCasePanel.test.js +115 -0
- package/components/routes/cases/detail/TaskPanel.d.ts +7 -0
- package/components/routes/cases/detail/TaskPanel.js +95 -0
- package/components/routes/cases/detail/TaskPanel.test.d.ts +1 -0
- package/components/routes/cases/detail/TaskPanel.test.js +145 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +11 -0
- package/components/routes/cases/detail/aggregates/CaseAggregate.js +24 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +6 -0
- package/components/routes/cases/detail/aggregates/SourceAggregate.js +26 -0
- package/components/routes/cases/detail/observables/Observable.d.ts +8 -0
- package/components/routes/cases/detail/observables/Observable.js +14 -0
- package/components/routes/cases/detail/observables/Observable.test.d.ts +1 -0
- package/components/routes/cases/detail/observables/Observable.test.js +80 -0
- package/components/routes/cases/detail/observables/ObservableTable.d.ts +7 -0
- package/components/routes/cases/detail/observables/ObservableTable.js +61 -0
- package/components/routes/cases/detail/observables/ObservableTable.test.d.ts +1 -0
- package/components/routes/cases/detail/observables/ObservableTable.test.js +97 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +22 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.js +105 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.test.d.ts +1 -0
- package/components/routes/cases/detail/sidebar/CaseFolder.test.js +98 -0
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +32 -0
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +99 -0
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +1 -0
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +117 -0
- package/components/routes/cases/detail/sidebar/FolderEntry.d.ts +25 -0
- package/components/routes/cases/detail/sidebar/FolderEntry.js +89 -0
- package/components/routes/cases/detail/sidebar/FolderEntry.test.d.ts +1 -0
- package/components/routes/cases/detail/sidebar/FolderEntry.test.js +81 -0
- package/components/routes/cases/detail/sidebar/RootDropZone.d.ts +5 -0
- package/components/routes/cases/detail/sidebar/RootDropZone.js +33 -0
- package/components/routes/cases/detail/sidebar/types.d.ts +9 -0
- package/components/routes/cases/detail/sidebar/utils.d.ts +10 -0
- package/components/routes/cases/detail/sidebar/utils.js +51 -0
- package/components/routes/cases/detail/sidebar/utils.test.d.ts +1 -0
- package/components/routes/cases/detail/sidebar/utils.test.js +82 -0
- package/components/routes/cases/detail/types.d.ts +22 -0
- package/components/routes/cases/detail/utils.d.ts +28 -0
- package/components/routes/cases/detail/utils.js +117 -0
- package/components/routes/cases/detail/utils.test.d.ts +1 -0
- package/components/routes/cases/detail/utils.test.js +166 -0
- package/components/routes/cases/hooks/useCase.d.ts +13 -0
- package/components/routes/cases/hooks/useCase.js +67 -0
- package/components/routes/cases/hooks/useCase.test.d.ts +1 -0
- package/components/routes/cases/hooks/useCase.test.js +170 -0
- package/components/routes/cases/modals/AddRecordToCaseModal.d.ts +7 -0
- package/components/routes/cases/modals/AddRecordToCaseModal.js +57 -0
- package/components/routes/cases/modals/AddRecordToCaseModal.test.d.ts +1 -0
- package/components/routes/cases/modals/AddRecordToCaseModal.test.js +309 -0
- package/components/routes/cases/modals/AddToCaseModal.d.ts +9 -0
- package/components/routes/cases/modals/AddToCaseModal.js +72 -0
- package/components/routes/cases/modals/CaseRecordRow.d.ts +9 -0
- package/components/routes/cases/modals/CaseRecordRow.js +14 -0
- package/components/routes/cases/modals/CreateCaseModal.d.ts +7 -0
- package/components/routes/cases/modals/CreateCaseModal.js +53 -0
- package/components/routes/cases/modals/CreateCaseModal.test.d.ts +1 -0
- package/components/routes/cases/modals/CreateCaseModal.test.js +319 -0
- package/components/routes/cases/modals/RenameItemModal.d.ts +9 -0
- package/components/routes/cases/modals/RenameItemModal.js +44 -0
- package/components/routes/cases/modals/ResolveModal.d.ts +7 -0
- package/components/routes/cases/modals/ResolveModal.js +120 -0
- package/components/routes/cases/modals/ResolveModal.test.d.ts +1 -0
- package/components/routes/cases/modals/ResolveModal.test.js +520 -0
- package/components/routes/cases/modals/hooks.d.ts +7 -0
- package/components/routes/cases/modals/hooks.js +40 -0
- package/components/routes/cases/modals/types.d.ts +13 -0
- package/components/routes/cases/search/CaseAssigneeFilter.d.ts +6 -0
- package/components/routes/cases/search/CaseAssigneeFilter.js +33 -0
- package/components/routes/cases/search/CaseAssigneeFilter.test.d.ts +1 -0
- package/components/routes/cases/search/CaseAssigneeFilter.test.js +127 -0
- package/components/routes/cases/search/CaseDateFilter.d.ts +13 -0
- package/components/routes/cases/search/CaseDateFilter.js +26 -0
- package/components/routes/cases/search/CaseDateFilter.test.d.ts +1 -0
- package/components/routes/cases/search/CaseDateFilter.test.js +115 -0
- package/components/routes/cases/search/CaseStatusFilter.d.ts +6 -0
- package/components/routes/cases/search/CaseStatusFilter.js +13 -0
- package/components/routes/cases/search/CaseStatusFilter.test.d.ts +1 -0
- package/components/routes/cases/search/CaseStatusFilter.test.js +86 -0
- package/components/routes/cases/utils.d.ts +3 -0
- package/components/routes/cases/utils.js +21 -0
- package/components/routes/dossiers/DossierEditor.js +4 -4
- package/components/routes/dossiers/DossierEditor.test.js +2 -2
- package/components/routes/dossiers/Dossiers.js +16 -14
- package/components/routes/dossiers/PivotForm.js +2 -4
- package/components/routes/events/EventViewer.d.ts +7 -0
- package/components/routes/events/EventViewer.js +24 -0
- package/components/routes/help/ApiDocumentation.js +2 -2
- package/components/routes/help/HitBannerDocumentation.js +2 -1
- package/components/routes/help/HitDocumentation.js +1 -3
- package/components/routes/help/HitSchemaDocumentation.js +1 -1
- package/components/routes/help/OverviewDocumentation.d.ts +1 -1
- package/components/routes/help/OverviewDocumentation.js +0 -1
- package/components/routes/help/RetentionDocumentation.js +2 -2
- package/components/routes/help/SearchDocumentation.js +1 -1
- package/components/routes/help/markdown/en/client.md.js +1 -1
- package/components/routes/help/markdown/fr/client.md.js +1 -1
- package/components/routes/hits/search/InformationPane.d.ts +1 -0
- package/components/routes/hits/search/InformationPane.js +52 -65
- package/components/routes/hits/search/QuerySettings.js +2 -1
- package/components/routes/hits/search/QuerySettings.test.js +19 -17
- package/components/routes/hits/search/{HitBrowser.js → RecordBrowser.js} +13 -12
- package/components/routes/hits/search/{HitQuery.d.ts → RecordQuery.d.ts} +2 -2
- package/components/routes/hits/search/{HitQuery.js → RecordQuery.js} +6 -6
- package/components/routes/hits/search/SearchPane.js +28 -50
- package/components/routes/hits/search/ViewLink.js +7 -6
- package/components/routes/hits/search/ViewLink.test.js +8 -9
- package/components/routes/hits/search/grid/RecordGrid.d.ts +3 -0
- package/components/routes/hits/search/grid/RecordGrid.js +64 -0
- package/components/routes/hits/search/shared/CustomSort.js +1 -1
- package/components/routes/hits/search/shared/CustomSpan.test.js +3 -4
- package/components/routes/hits/search/shared/HitFilter.d.ts +1 -1
- package/components/routes/hits/search/shared/HitSort.d.ts +1 -1
- package/components/routes/hits/search/shared/HitSort.js +2 -1
- package/components/routes/hits/search/shared/IndexPicker.d.ts +8 -0
- package/components/routes/hits/search/shared/IndexPicker.js +21 -0
- package/components/routes/hits/search/shared/LayoutSettings.js +25 -0
- package/components/routes/hits/search/shared/SearchActionMenu.d.ts +4 -0
- package/components/routes/hits/search/shared/SearchActionMenu.js +11 -0
- package/components/routes/hits/search/shared/SearchSpan.d.ts +1 -1
- package/components/routes/hits/search/shared/SearchSpan.js +1 -1
- package/components/routes/hits/view/HitViewer.js +16 -17
- package/components/routes/home/AddNewCard.js +2 -2
- package/components/routes/home/AddNewCard.test.js +20 -2
- package/components/routes/home/AnalyticCard.js +1 -1
- package/components/routes/home/EntryWrapper.js +1 -1
- package/components/routes/home/HomeSettings.js +1 -1
- package/components/routes/home/HomeSettings.test.d.ts +1 -0
- package/components/routes/home/HomeSettings.test.js +44 -0
- package/components/routes/home/ViewCard.js +52 -46
- package/components/routes/home/ViewRefresh.test.js +1 -1
- package/components/routes/home/index.js +2 -2
- package/components/routes/overviews/OverviewCard.js +1 -1
- package/components/routes/overviews/OverviewViewer.js +13 -10
- package/components/routes/overviews/Overviews.js +16 -14
- package/components/routes/overviews/markdownExtendedTokenProvider.js +15 -15
- package/components/routes/overviews/markdownExtendedTokenProvider.test.d.ts +1 -0
- package/components/routes/overviews/markdownExtendedTokenProvider.test.js +50 -0
- package/components/routes/settings/ProfileSection.js +2 -2
- package/components/routes/templates/TemplateEditor.d.ts +3 -2
- package/components/routes/templates/TemplateEditor.js +1 -1
- package/components/routes/templates/TemplateViewer.js +10 -5
- package/components/routes/templates/Templates.js +19 -14
- package/components/routes/views/ViewComposer.js +78 -26
- package/components/routes/views/Views.js +17 -14
- package/i18n.js +1 -1
- package/index.css +4 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/locales/en/translation.json +188 -34
- package/locales/fr/translation.json +186 -32
- package/models/WithMetadata.d.ts +2 -1
- package/models/entities/generated/Analytic.d.ts +0 -3
- package/models/entities/generated/ApiType.d.ts +3 -1
- package/models/entities/generated/AttachmentsFile.d.ts +12 -0
- package/models/entities/generated/Case.d.ts +29 -0
- package/models/entities/generated/Column.d.ts +7 -0
- package/models/entities/generated/DestinationOriginal.d.ts +19 -0
- package/models/entities/generated/ECSEvent.d.ts +31 -0
- package/models/entities/generated/EmailAttachment.d.ts +8 -0
- package/models/entities/generated/EmailParent.d.ts +19 -0
- package/models/entities/generated/Enrichments.d.ts +7 -0
- package/models/entities/generated/EnrichmentsIndicator.d.ts +21 -0
- package/models/entities/generated/Event.d.ts +80 -26
- package/models/entities/generated/EventCloud.d.ts +20 -0
- package/models/entities/generated/EventDestination.d.ts +23 -0
- package/models/entities/generated/EventEmail.d.ts +30 -0
- package/models/entities/generated/EventFile.d.ts +36 -0
- package/models/entities/generated/EventHowler.d.ts +42 -0
- package/models/entities/generated/EventHttp.d.ts +11 -0
- package/models/entities/generated/EventObserver.d.ts +21 -0
- package/models/entities/generated/EventOrganization.d.ts +7 -0
- package/models/entities/generated/EventProcess.d.ts +34 -0
- package/models/entities/generated/EventSource.d.ts +23 -0
- package/models/entities/generated/EventThreat.d.ts +21 -0
- package/models/entities/generated/EventTls.d.ts +12 -0
- package/models/entities/generated/Hit.d.ts +3 -2
- package/models/entities/generated/Host.d.ts +1 -0
- package/models/entities/generated/Howler.d.ts +1 -6
- package/models/entities/generated/HttpResponse.d.ts +11 -0
- package/models/entities/generated/Item.d.ts +12 -0
- package/models/entities/generated/ObserverIngress.d.ts +9 -0
- package/models/entities/generated/Rule.d.ts +8 -9
- package/models/entities/generated/Settings.d.ts +4 -0
- package/models/entities/generated/Task.d.ts +10 -0
- package/models/entities/generated/Threat.d.ts +2 -2
- package/models/entities/generated/{Enrichment.d.ts → ThreatEnrichment.d.ts} +1 -1
- package/models/entities/generated/View.d.ts +1 -0
- package/models/socket/CaseUpdate.d.ts +5 -0
- package/models/socket/ViewersUpdate.d.ts +4 -0
- package/package.json +156 -137
- package/plugins/clue/components/ClueLeadForm.js +1 -1
- package/plugins/clue/components/CluePivotForm.js +2 -2
- package/plugins/clue/components/ClueTypography.js +2 -2
- package/plugins/clue/helpers.js +4 -4
- package/plugins/clue/index.d.ts +3 -2
- package/plugins/clue/index.js +0 -1
- package/plugins/clue/utils.d.ts +2 -1
- package/rest/AxiosClient.test.d.ts +1 -0
- package/rest/AxiosClient.test.js +185 -0
- package/tests/mocks.d.ts +11 -1
- package/tests/mocks.js +12 -7
- package/tests/server-handlers.js +11 -1
- package/tests/utils.d.ts +4 -0
- package/tests/utils.js +21 -0
- package/utils/Throttler.test.js +2 -2
- package/utils/actionUtils.d.ts +3 -3
- package/utils/classificationParser.d.ts +195 -0
- package/utils/classificationParser.js +680 -0
- package/utils/constants.d.ts +4 -3
- package/utils/constants.js +6 -0
- package/utils/localStorage.js +1 -1
- package/utils/menuUtils.js +0 -4
- package/utils/recordFunctions.d.ts +3 -0
- package/utils/recordFunctions.js +8 -0
- package/utils/recordFunctions.test.d.ts +1 -0
- package/utils/{hitFunctions.test.js → recordFunctions.test.js} +1 -1
- package/utils/socketUtils.d.ts +14 -0
- package/utils/socketUtils.js +17 -1
- package/utils/socketUtils.test.js +49 -16
- package/utils/stringUtils.d.ts +1 -0
- package/utils/stringUtils.js +7 -0
- package/utils/stringUtils.test.js +22 -2
- package/utils/typeUtils.d.ts +7 -0
- package/utils/typeUtils.js +27 -0
- package/utils/utils.d.ts +1 -1
- package/utils/utils.js +2 -2
- package/utils/utils.test.js +119 -2
- package/utils/viewUtils.js +3 -0
- package/utils/xsrf.js +1 -1
- package/api/analytic/rules.d.ts +0 -3
- package/api/analytic/rules.js +0 -8
- package/components/app/providers/HitProvider.d.ts +0 -22
- package/components/elements/display/icons/BundleButton.d.ts +0 -6
- package/components/elements/display/icons/BundleButton.js +0 -32
- package/components/elements/hit/HitRelated.d.ts +0 -6
- package/components/elements/hit/HitRelated.js +0 -7
- package/components/elements/hit/elements/HitUsers.js +0 -33
- package/components/routes/advanced/RuleModal.d.ts +0 -7
- package/components/routes/advanced/RuleModal.js +0 -83
- package/components/routes/analytics/RuleView.d.ts +0 -8
- package/components/routes/analytics/RuleView.js +0 -48
- package/components/routes/help/BundleDocumentation.d.ts +0 -3
- package/components/routes/help/BundleDocumentation.js +0 -12
- package/components/routes/help/markdown/en/bundles.md.js +0 -1
- package/components/routes/help/markdown/fr/bundles.md.js +0 -1
- package/components/routes/hits/search/BundleParentMenu.d.ts +0 -6
- package/components/routes/hits/search/BundleParentMenu.js +0 -32
- package/components/routes/hits/search/BundleScroller.d.ts +0 -2
- package/components/routes/hits/search/BundleScroller.js +0 -6
- package/components/routes/hits/search/HitContextMenu.js +0 -239
- package/components/routes/hits/search/LayoutSettings.js +0 -24
- package/components/routes/hits/search/grid/HitGrid.d.ts +0 -3
- package/components/routes/hits/search/grid/HitGrid.js +0 -132
- package/utils/hitFunctions.d.ts +0 -2
- package/utils/hitFunctions.js +0 -8
- /package/{components/app/providers/HitSearchProvider.test.d.ts → api/socket/viewers.test.d.ts} +0 -0
- /package/components/{routes/hits/search/HitContextMenu.test.d.ts → app/providers/GridColumnsProvider.test.d.ts} +0 -0
- /package/{utils/hitFunctions.test.d.ts → components/app/providers/RecordSearchProvider.test.d.ts} +0 -0
- /package/components/{routes/overviews/OverviewEditor.d.ts → elements/MarkdownEditor.d.ts} +0 -0
- /package/components/{routes/hits/search → elements/hit}/grid/AddColumnModal.d.ts +0 -0
- /package/components/elements/hit/{HitDetails.d.ts → related/RelatedRecords.d.ts} +0 -0
- /package/components/routes/hits/search/{HitBrowser.d.ts → RecordBrowser.d.ts} +0 -0
- /package/components/routes/hits/search/{LayoutSettings.d.ts → shared/LayoutSettings.d.ts} +0 -0
|
@@ -25,7 +25,7 @@ const ApiDocumentation = () => {
|
|
|
25
25
|
const isLg = useMediaQuery(theme.breakpoints.down('lg'));
|
|
26
26
|
const [data, setData] = useState(null);
|
|
27
27
|
useEffect(() => {
|
|
28
|
-
dispatchApi(api.help.get()).then(setData);
|
|
28
|
+
void dispatchApi(api.help.get()).then(setData);
|
|
29
29
|
}, [dispatchApi]);
|
|
30
30
|
if (!data) {
|
|
31
31
|
return (_jsx(PageCenter, { margin: 4, maxWidth: "1750px", width: "100%", textAlign: "left", children: t('loading') }));
|
|
@@ -49,7 +49,7 @@ const ApiDocumentation = () => {
|
|
|
49
49
|
.replace(/(\S+)\s+=>\s+(.+)/g, '\n`$1`: $2\n')
|
|
50
50
|
.replace(/(Data Block:\n)([\s\S]+)(Result Example:)/, (__, p1, p2, p3) => `${p1}\`\`\`\n${p2.trim()}\n\`\`\`\n${p3}`)
|
|
51
51
|
.replace(/(Result Example:\n)([\s\S]+)$/, (__, p1, p2) => `${p1}\`\`\`\n${p2.trim()}\n\`\`\``) }));
|
|
52
|
-
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { style: { marginBottom: '1rem' }, sx: [isLg && { '& > td': { borderBottom: 0 } }], children: [_jsx(TableCell, { children: _jsxs(Stack, { direction: "column", spacing: 1, alignItems: "start", children: [_jsx(Typography, { children: endpoint.name }), _jsx("code", { children: endpoint.path }), _jsxs(Stack, { direction: "row", spacing: 1, children: [endpoint.complete ? (_jsx(Chip, {
|
|
52
|
+
return (_jsxs(Fragment, { children: [_jsxs(TableRow, { style: { marginBottom: '1rem' }, sx: [isLg && { '& > td': { borderBottom: 0 } }], children: [_jsx(TableCell, { children: _jsxs(Stack, { direction: "column", spacing: 1, alignItems: "start", children: [_jsx(Typography, { children: endpoint.name }), _jsx("code", { children: endpoint.path }), _jsxs(Stack, { direction: "row", spacing: 1, children: [endpoint.complete ? (_jsx(Chip, { label: "Stable", color: "success" })) : (_jsx(Chip, { label: "Unstable", color: "error" })), endpoint.protected ? (_jsx(Chip, { label: "Protected", color: "warning" })) : (_jsx(Chip, { label: "Unprotected" }))] }), _jsx(Stack, { spacing: 1, direction: "row", children: endpoint.methods.map(m => (_jsx(Chip, { size: "small", label: m }, m))) }), endpoint.ui_only && _jsx(Chip, { label: "UI Only" })] }) }), _jsx(TableCell, { children: _jsx(Stack, { spacing: 1, direction: "row", children: endpoint.required_type.map(type => (_jsx(Chip, { size: "small", label: type, color: user.roles?.includes(type) ? 'success' : 'default' }, type))) }) }), _jsx(TableCell, { children: _jsx(Stack, { spacing: 1, direction: "row", children: endpoint.required_priv.map((p) => (_jsx(Chip, { size: "small", label: t(APIKEY_LABELS[p]) }, p))) }) }), !isLg && _jsx(TableCell, { children: documentationCell })] }), isLg && (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { '& pre': { whiteSpace: 'pre-wrap' } }, children: documentationCell }) }))] }, endpoint.id));
|
|
53
53
|
}) })] }) })] }) }));
|
|
54
54
|
};
|
|
55
55
|
export default ApiDocumentation;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { CardContent, Divider, Typography } from '@mui/material';
|
|
3
3
|
import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
|
|
4
4
|
import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
|
|
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
9
9
|
const HitBannerDocumentation = () => {
|
|
10
10
|
const { t } = useTranslation();
|
|
11
11
|
const dummyHit = useMemo(() => ({
|
|
12
|
+
__index: 'hit',
|
|
12
13
|
timestamp: '2023-02-11T15:10:31.585826Z',
|
|
13
14
|
howler: {
|
|
14
15
|
id: 'howler.id',
|
|
@@ -5,7 +5,6 @@ import { useScrollRestoration } from '@cccsaurora/howler-ui/components/hooks/use
|
|
|
5
5
|
import { useCallback, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { useSearchParams } from 'react-router-dom';
|
|
8
|
-
import BundleDocumentation from './BundleDocumentation';
|
|
9
8
|
import HelpTabs from './components/HelpTabs';
|
|
10
9
|
import HitBannerDocumentation from './HitBannerDocumentation';
|
|
11
10
|
import HitLabelsDocumentation from './HitLabelsDocumentation';
|
|
@@ -23,8 +22,7 @@ const HitDocumentation = () => {
|
|
|
23
22
|
searchParams.set('tab', _tab);
|
|
24
23
|
setSearchParams(new URLSearchParams(searchParams));
|
|
25
24
|
}, [searchParams, setSearchParams]);
|
|
26
|
-
return (_jsx(PageCenter, { margin: 4, width: "100%", maxWidth: "1750px", textAlign: "left", children: _jsxs(Stack, { sx: { flexDirection: useHorizontal ? 'column' : 'row', '& h1': { mt: 0 } }, children: [_jsxs(HelpTabs, { value: tab, children: [_jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.schema.title') }), value: "schema", onClick: () => onChange('schema') }), _jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.banner.title') }), value: "header", onClick: () => onChange('header') }), _jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.
|
|
27
|
-
bundle: () => _jsx(BundleDocumentation, {}),
|
|
25
|
+
return (_jsx(PageCenter, { margin: 4, width: "100%", maxWidth: "1750px", textAlign: "left", children: _jsxs(Stack, { sx: { flexDirection: useHorizontal ? 'column' : 'row', '& h1': { mt: 0 } }, children: [_jsxs(HelpTabs, { value: tab, children: [_jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.schema.title') }), value: "schema", onClick: () => onChange('schema') }), _jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.banner.title') }), value: "header", onClick: () => onChange('header') }), _jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.links.title') }), value: "links", onClick: () => onChange('links') }), _jsx(Tab, { label: _jsx(Typography, { variant: "caption", children: t('help.hit.labels.title') }), value: "labels", onClick: () => onChange('labels') })] }), _jsx(Box, { children: {
|
|
28
26
|
header: () => _jsx(HitBannerDocumentation, {}),
|
|
29
27
|
links: () => _jsx(HitLinksDocumentation, {}),
|
|
30
28
|
labels: () => _jsx(HitLabelsDocumentation, {}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Clear } from '@mui/icons-material';
|
|
3
3
|
import { Alert, AlertTitle, Box, Chip, IconButton, InputAdornment, Stack, Table, TableBody, TableCell, TableHead, TableRow, TextField, Typography, useTheme } from '@mui/material';
|
|
4
4
|
import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown';
|
|
@@ -2,7 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
3
3
|
import HandlebarsMarkdown from '@cccsaurora/howler-ui/components/elements/display/HandlebarsMarkdown';
|
|
4
4
|
import { useScrollRestoration } from '@cccsaurora/howler-ui/components/hooks/useScrollRestoration';
|
|
5
|
-
import {} from 'react';
|
|
6
5
|
import ErrorBoundary from '../ErrorBoundary';
|
|
7
6
|
import { useStartingTemplate } from '../overviews/startingTemplate';
|
|
8
7
|
const OverviewDocumentation = () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
|
|
3
3
|
import Markdown from '@cccsaurora/howler-ui/components/elements/display/Markdown';
|
|
4
4
|
import { useScrollRestoration } from '@cccsaurora/howler-ui/components/hooks/useScrollRestoration';
|
|
@@ -25,7 +25,7 @@ const RetentionDocumentation = () => {
|
|
|
25
25
|
return modifyDocumentation(original, howlerPluginStore, pluginStore);
|
|
26
26
|
}, [i18n.language, pluginStore]);
|
|
27
27
|
useEffect(() => {
|
|
28
|
-
api.search.hit
|
|
28
|
+
void api.search.hit
|
|
29
29
|
.post({
|
|
30
30
|
query: 'howler.id:*',
|
|
31
31
|
sort: 'event.created asc',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ExpandMore } from '@mui/icons-material';
|
|
3
3
|
import { Accordion, AccordionDetails, AccordionSummary, Box, Card, Chip, emphasize, Stack, styled, Tab, Table, TableBody, TableCell, TableHead, TableRow, Typography, useMediaQuery, useTheme } from '@mui/material';
|
|
4
4
|
import { useAppBar, useAppLayout, useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default "# Howler Client Documentation\n\nThis documentation will outline how to interact with the howler API using the howler client in both Java and python development environments. We will outline the basic process of creating a new hit in each environment as well as searching howler for hits matching your query.\n\n## Getting started\n\n### Installation\n\nIn order to use the howler client, you need to list it as a dependency in your project.\n\n#### **Python**\n\nSimply install through pip:\n\n```bash\npip install howler-client\n```\n\nYou can also add it to your requirements.txt, or whatever dependency management system you use.\n\n### Authentication\n\nAs outlined in the [Authentication Documentation](/help/auth), there's a number of ways users can choose to authenticate. In order to interface with the howler client, however, the suggested flow is to use an API key. So before we start, let's generate a key.\n\n1. Open the Howler UI you'd like to interface with.\n2. Log in, then click your profile in the top right.\n3. Under user menu, click Settings.\n4. Under User Security, press the (+) icon on the API Keys row.\n5. Name your key, and give it the requisite permissions.\n6. Press Create, and copy the supplied string somewhere safe. **You will not see this string again.**\n\nThis API Key will be supplied to your code later on.\n\n## Python Client\n\nIn order to connect with howler using the python client, there is a fairly simple process to follow:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtain this from the user settings page of the Howler UI\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\n```alert\nYou can skip generating an API Key and providing it if you're executing this code within HOGWARTS (i.e., on jupyterhub or airflow). OBO will handle authentication for you!\n```\n\nThat's it! You can now use the `howler` object to interact with the server. So what does that actually look like?\n\n### Creating hits in Python\n\nFor the python client, you can create hits using either the `howler.hit.create` or `howler.hit.create_from_map` functions.\n\n#### `create`\n\nThis function takes in a single argument - either a single hit, or a list of them, conforming to the [Howler Schema](/help/hit?tab=schema). Here is a simple example:\n\n```python\n# Some bogus data in the Howler Schema format\nexample_hit = {\n \"howler\": {\n \"analytic\": \"example\",\n \"score\": 10.0\n },\n \"event\": {\n \"reason\": \"Example hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nYou can also ingest data in a flat format:\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: The name of the analytic creating the hit\n- `map`: A mapping between the raw data you have and the howler schema\n - The format is a dictionary where the keys are the flattened path of the raw data, and the values are a list of flattened paths for Howler's fields where the data will be copied into.\n- `documents`: The raw data you want to add to howler\n\nHere is a simple example:\n\n```python\n# The mapping from our data to howler's schema\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Some bogus data in a custom format we want to add to howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Note that the third argument is of type list!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Querying Hits\n\nQuerying hits using the howler python client is done using the `howler.search.hit` function. It has a number of required and optional arguments:\n\n- Required:\n - `query`: lucene query (string)\n- Optional:\n - `filters`: Additional lucene queries used to filter the data (list of strings)\n - `fl`: List of fields to return (comma separated string of fields)\n - `offset`: Offset at which the query items should start (integer)\n - `rows`: Number of records to return (integer)\n - `sort`: Field used for sorting with direction (string: ex. 'id desc')\n - `timeout`: Max amount of milliseconds the query will run (integer)\n - `
|
|
1
|
+
export default "# Howler Client Documentation\n\nThis documentation will outline how to interact with the howler API using the howler client in both Java and python development environments. We will outline the basic process of creating a new hit in each environment as well as searching howler for hits matching your query.\n\n## Getting started\n\n### Installation\n\nIn order to use the howler client, you need to list it as a dependency in your project.\n\n#### **Python**\n\nSimply install through pip:\n\n```bash\npip install howler-client\n```\n\nYou can also add it to your requirements.txt, or whatever dependency management system you use.\n\n### Authentication\n\nAs outlined in the [Authentication Documentation](/help/auth), there's a number of ways users can choose to authenticate. In order to interface with the howler client, however, the suggested flow is to use an API key. So before we start, let's generate a key.\n\n1. Open the Howler UI you'd like to interface with.\n2. Log in, then click your profile in the top right.\n3. Under user menu, click Settings.\n4. Under User Security, press the (+) icon on the API Keys row.\n5. Name your key, and give it the requisite permissions.\n6. Press Create, and copy the supplied string somewhere safe. **You will not see this string again.**\n\nThis API Key will be supplied to your code later on.\n\n## Python Client\n\nIn order to connect with howler using the python client, there is a fairly simple process to follow:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtain this from the user settings page of the Howler UI\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\n```alert\nYou can skip generating an API Key and providing it if you're executing this code within HOGWARTS (i.e., on jupyterhub or airflow). OBO will handle authentication for you!\n```\n\nThat's it! You can now use the `howler` object to interact with the server. So what does that actually look like?\n\n### Creating hits in Python\n\nFor the python client, you can create hits using either the `howler.hit.create` or `howler.hit.create_from_map` functions.\n\n#### `create`\n\nThis function takes in a single argument - either a single hit, or a list of them, conforming to the [Howler Schema](/help/hit?tab=schema). Here is a simple example:\n\n```python\n# Some bogus data in the Howler Schema format\nexample_hit = {\n \"howler\": {\n \"analytic\": \"example\",\n \"score\": 10.0\n },\n \"event\": {\n \"reason\": \"Example hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nYou can also ingest data in a flat format:\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: The name of the analytic creating the hit\n- `map`: A mapping between the raw data you have and the howler schema\n - The format is a dictionary where the keys are the flattened path of the raw data, and the values are a list of flattened paths for Howler's fields where the data will be copied into.\n- `documents`: The raw data you want to add to howler\n\nHere is a simple example:\n\n```python\n# The mapping from our data to howler's schema\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Some bogus data in a custom format we want to add to howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Note that the third argument is of type list!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Querying Hits\n\nQuerying hits using the howler python client is done using the `howler.search.hit` function. It has a number of required and optional arguments:\n\n- Required:\n - `query`: lucene query (string)\n- Optional:\n - `filters`: Additional lucene queries used to filter the data (list of strings)\n - `fl`: List of fields to return (comma separated string of fields)\n - `offset`: Offset at which the query items should start (integer)\n - `rows`: Number of records to return (integer)\n - `sort`: Field used for sorting with direction (string: ex. 'id desc')\n - `timeout`: Max amount of milliseconds the query will run (integer)\n - `track_total_hits`: Number of hits to track (default: 10k)\n\nHere are some example queries:\n\n```python\n# Search for all hits created by assemblyline, show the first 50, and return only their ids\nhowler.search.hit(\"howler.analytic:assemblyline\", fl=\"howler.id\", rows=50)\n\n# Search for all resolved hits created in the last five days, returning their id and the analytic that created them. Show only ten, offset by 40\nhowler.search.hit(\"howler.status:resolved\", filters=['event.created:[now-5d TO now]'] fl=\"howler.id,howler.analytic\", rows=10, offset=40)\n\n# Search for all hits, timeout if the query takes more than 100ms\nhowler.search.hit(\"howler.id:*\", track_total_hits=100000000, timeout=100)\n```\n\n### Updating Hits\n\nIn order to update hits, there are a number of supported functions:\n\n- `howler.hit.update(...)`\n- `howler.hit.update_by_query(...)`\n- `howler.hit.overwrite(...)`\n\n#### `update()`\n\nIf you want to update a hit in a transactional way, you can use the following code:\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.update(hit_to_update[\"howler\"][\"id\"], [(UPDATE_SET, \"howler.score\", hit_to_update[\"howler\"][\"score\"] + 100)])\n```\n\nThe following operations can be run to update a hit.\n\n**List Operations:**\n\n- `UPDATE_APPEND`: Used to append a value to a given list\n- `UPDATE_APPEND_IF_MISSING`: Used to append a value to a given list if the value isn't already in the list\n- `UPDATE_REMOVE`: Will remove a given value from a list\n\n**Numeric Operations:**\n\n- `UPDATE_DEC`: Decrement a numeric value by the specified amount\n- `UPDATE_INC`: Increment a numeric value by the specified amount\n- `UPDATE_MAX`: Will set a numeric value to the maximum of the existing value and the specified value\n- `UPDATE_MIN`: Will set a numeric value to the minimum of the existing value and the specified value\n\n**Multipurpose Operations:**\n\n- `UPDATE_SET`: Set a field's value to the given value\n- `UPDATE_DELETE`: Will delete a given field's value\n\n#### `update_by_query()`\n\nThis function allows you to update a large number of hits by a query:\n\n```python\nclient.hit.update_by_query(f'howler.analytic:\"Example Alert\"', [(UPDATE_INC, \"howler.score\", 100)])\n```\n\nThe same operations as in `update()` can be used.\n\n### `overwrite()`\n\nThis function allows you to directly overwrite a hit with a partial hit object. This is the most easy to use, but loses some of the validation and additional processing of the update functions.\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.overwrite(hit_to_update[\"howler\"][\"id\"], {\"source.ip\": \"127.0.0.1\", \"destination.ip\": \"8.8.8.8\"})\n```\n"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default "# Documentation du client Howler\n\nCette documentation explique comment interagir avec l'API howler en utilisant le client howler dans les environnements de d\u00e9veloppement Java et Python. Nous d\u00e9crirons le processus de base de la cr\u00e9ation d'un nouveau hit dans chaque environnement ainsi que la recherche dans howler des hits correspondant \u00e0 votre requ\u00eate.\n\n## Mise en route\n\n### Installation\n\nAfin d'utiliser le client howler, vous devez le lister comme une d\u00e9pendance dans votre projet.\n\n#### **Python**\n\nIl suffit de l'installer \u00e0 l'aide de pip:\n\n```bash\npip install howler-client\n```\n\nVous pouvez \u00e9galement l'ajouter \u00e0 votre requirements.txt, ou \u00e0 tout autre syst\u00e8me de gestion des d\u00e9pendances que vous utilisez.\n\n### Authentification\n\nComme indiqu\u00e9 dans la [Documentation sur l'authentification](/help/auth), les utilisateurs peuvent s'authentifier de diff\u00e9rentes mani\u00e8res. Pour interfacer avec le client howler, cependant, le flux sugg\u00e9r\u00e9 est d'utiliser une cl\u00e9 API. Avant de commencer, g\u00e9n\u00e9rons une cl\u00e9.\n\n1. Ouvrez l'interface utilisateur Howler avec laquelle vous souhaitez vous interfacer.\n2. Connectez-vous, puis cliquez sur votre profil en haut \u00e0 droite.\n3. Dans le menu utilisateur, cliquez sur Param\u00e8tres.\n4. Sous S\u00e9curit\u00e9 de l'utilisateur, appuyez sur l'ic\u00f4ne (+) sur la ligne Cl\u00e9s API.\n5. Nommez votre cl\u00e9 et donnez-lui les autorisations n\u00e9cessaires.\n6. Appuyez sur Create (Cr\u00e9er) et copiez la cha\u00eene fournie dans un endroit s\u00fbr. \\*\\*Vous ne reverrez plus cette cha\u00eene.\n\nCette cl\u00e9 API sera fournie \u00e0 votre code par la suite.\n\n## Client Python\n\nPour se connecter \u00e0 howler en utilisant le client python, il y a un processus assez simple \u00e0 suivre:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtenez-le \u00e0 partir de la page des param\u00e8tres de l'utilisateur de l'interface utilisateur de Howler.\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\nVoil\u00e0, c'est fait ! Vous pouvez maintenant utiliser l'objet `howler` pour interagir avec le serveur. A quoi cela ressemble-t-il en r\u00e9alit\u00e9?\n\n### Cr\u00e9er des hits en Python\n\nPour le client python, vous pouvez cr\u00e9er des hits en utilisant les fonctions `howler.hit.create` ou `howler.hit.create_from_map`.\n\n#### `create`\n\nCette fonction prend un seul argument - soit un hit unique, soit une liste de hits, conforme au [Howler Schema] (/help/hit?tab=schema). Voici un exemple simple :\n\n```python\n# Quelques donn\u00e9es bidons au format Howler Schema\nexemple_hit = {\n \"howler\" : {\n \"analytic\" : \"exemple\",\n \"score\" : 10.0\n },\n \"event\" : {\n \"reason\" : \"Exemple hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nVous pouvez \u00e9galement ing\u00e9rer des donn\u00e9es dans un format plat :\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: Le nom de l'outil d'analyse qui cr\u00e9e le hit\n- `map`: Une correspondance entre les donn\u00e9es brutes que vous avez et le sch\u00e9ma howler\n - Le format est un dictionnaire o\u00f9 les cl\u00e9s sont le chemin aplati des donn\u00e9es brutes, et les valeurs sont une liste de chemins aplatis pour les champs de Howler dans lesquels les donn\u00e9es seront copi\u00e9es.\n- `documents`: Les donn\u00e9es brutes que vous voulez ajouter \u00e0 Howler.\n\nVoici un exemple simple:\n\n```python\n# La correspondance entre nos donn\u00e9es et le sch\u00e9ma de Howler\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Quelques fausses donn\u00e9es dans un format personnalis\u00e9 que nous voulons ajouter \u00e0 howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Notez que le troisi\u00e8me argument est de type liste!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Interroger les hits en Python\n\nL'interrogation des hits avec le client python howler se fait en utilisant la fonction `howler.search.hit`. Elle poss\u00e8de un certain nombre d'arguments obligatoires et optionnels:\n\n- Obligatoire:\n - `query`: requ\u00eate lucene (cha\u00eene)\n- Facultatif: `filters`: requ\u00eate lucene (cha\u00eene):\n - `filters`: Requ\u00eates lucene additionnelles utilis\u00e9es pour filtrer les donn\u00e9es (liste de cha\u00eenes)\n - `fl`: Liste des champs \u00e0 retourner (cha\u00eene de champs s\u00e9par\u00e9s par des virgules)\n - `offset`: Offset auquel les \u00e9l\u00e9ments de la requ\u00eate doivent commencer (entier)\n - `rows`: Nombre d'enregistrements \u00e0 retourner (entier)\n - `sort`: Champ utilis\u00e9 pour le tri avec direction (cha\u00eene: ex. 'id desc')\n - `timeout`: Nombre maximum de millisecondes d'ex\u00e9cution de la requ\u00eate (entier)\n - `
|
|
1
|
+
export default "# Documentation du client Howler\n\nCette documentation explique comment interagir avec l'API howler en utilisant le client howler dans les environnements de d\u00e9veloppement Java et Python. Nous d\u00e9crirons le processus de base de la cr\u00e9ation d'un nouveau hit dans chaque environnement ainsi que la recherche dans howler des hits correspondant \u00e0 votre requ\u00eate.\n\n## Mise en route\n\n### Installation\n\nAfin d'utiliser le client howler, vous devez le lister comme une d\u00e9pendance dans votre projet.\n\n#### **Python**\n\nIl suffit de l'installer \u00e0 l'aide de pip:\n\n```bash\npip install howler-client\n```\n\nVous pouvez \u00e9galement l'ajouter \u00e0 votre requirements.txt, ou \u00e0 tout autre syst\u00e8me de gestion des d\u00e9pendances que vous utilisez.\n\n### Authentification\n\nComme indiqu\u00e9 dans la [Documentation sur l'authentification](/help/auth), les utilisateurs peuvent s'authentifier de diff\u00e9rentes mani\u00e8res. Pour interfacer avec le client howler, cependant, le flux sugg\u00e9r\u00e9 est d'utiliser une cl\u00e9 API. Avant de commencer, g\u00e9n\u00e9rons une cl\u00e9.\n\n1. Ouvrez l'interface utilisateur Howler avec laquelle vous souhaitez vous interfacer.\n2. Connectez-vous, puis cliquez sur votre profil en haut \u00e0 droite.\n3. Dans le menu utilisateur, cliquez sur Param\u00e8tres.\n4. Sous S\u00e9curit\u00e9 de l'utilisateur, appuyez sur l'ic\u00f4ne (+) sur la ligne Cl\u00e9s API.\n5. Nommez votre cl\u00e9 et donnez-lui les autorisations n\u00e9cessaires.\n6. Appuyez sur Create (Cr\u00e9er) et copiez la cha\u00eene fournie dans un endroit s\u00fbr. \\*\\*Vous ne reverrez plus cette cha\u00eene.\n\nCette cl\u00e9 API sera fournie \u00e0 votre code par la suite.\n\n## Client Python\n\nPour se connecter \u00e0 howler en utilisant le client python, il y a un processus assez simple \u00e0 suivre:\n\n```python\nfrom howler_client import get_client\n\nUSERNAME = 'user' # Obtenez-le \u00e0 partir de la page des param\u00e8tres de l'utilisateur de l'interface utilisateur de Howler.\nAPIKEY = 'apikey_name:apikey_data'\n\napikey = (USERNAME, APIKEY)\n\nhowler = get_client(\"$CURRENT_URL\", apikey=apikey)\n```\n\nVoil\u00e0, c'est fait ! Vous pouvez maintenant utiliser l'objet `howler` pour interagir avec le serveur. A quoi cela ressemble-t-il en r\u00e9alit\u00e9?\n\n### Cr\u00e9er des hits en Python\n\nPour le client python, vous pouvez cr\u00e9er des hits en utilisant les fonctions `howler.hit.create` ou `howler.hit.create_from_map`.\n\n#### `create`\n\nCette fonction prend un seul argument - soit un hit unique, soit une liste de hits, conforme au [Howler Schema] (/help/hit?tab=schema). Voici un exemple simple :\n\n```python\n# Quelques donn\u00e9es bidons au format Howler Schema\nexemple_hit = {\n \"howler\" : {\n \"analytic\" : \"exemple\",\n \"score\" : 10.0\n },\n \"event\" : {\n \"reason\" : \"Exemple hit\"\n }\n}\n\nhowler.hit.create(example_hit)\n```\n\nVous pouvez \u00e9galement ing\u00e9rer des donn\u00e9es dans un format plat :\n\n```python\nexample_hit = {\n \"howler.analytic\": \"example\",\n \"howler.score\": 10.0,\n \"event.reason\": \"Example hit\"\n}\n\nhowler.hit.create(example_hit)\n```\n\n#### `create_from_map`\n\nThis function takes in three arguments:\n\n- `tool name`: Le nom de l'outil d'analyse qui cr\u00e9e le hit\n- `map`: Une correspondance entre les donn\u00e9es brutes que vous avez et le sch\u00e9ma howler\n - Le format est un dictionnaire o\u00f9 les cl\u00e9s sont le chemin aplati des donn\u00e9es brutes, et les valeurs sont une liste de chemins aplatis pour les champs de Howler dans lesquels les donn\u00e9es seront copi\u00e9es.\n- `documents`: Les donn\u00e9es brutes que vous voulez ajouter \u00e0 Howler.\n\nVoici un exemple simple:\n\n```python\n# La correspondance entre nos donn\u00e9es et le sch\u00e9ma de Howler\nhwl_map = {\n \"file.sha256\": [\"file.hash.sha256\", \"howler.hash\"],\n \"file.name\": [\"file.name\"],\n \"src_ip\": [\"source.ip\", \"related.ip\"],\n \"dest_ip\": [\"destination.ip\", \"related.ip\"],\n \"time.created\": [\"event.start\"],\n}\n\n# Quelques fausses donn\u00e9es dans un format personnalis\u00e9 que nous voulons ajouter \u00e0 howler\nexample_hit = {\n \"src_ip\": \"0.0.0.0\",\n \"dest_ip\": \"8.8.8.8\",\n \"file\": {\n \"name\": \"hello.exe\",\n \"sha256\": sha256(str(\"hello.exe\").encode()).hexdigest()\n },\n \"time\": {\n \"created\": datetime.now().isoformat()\n },\n}\n\n# Notez que le troisi\u00e8me argument est de type liste!\nhowler.hit.create_from_map(\"example_ingestor\", hwl_map, [example_hit])\n```\n\n### Interroger les hits en Python\n\nL'interrogation des hits avec le client python howler se fait en utilisant la fonction `howler.search.hit`. Elle poss\u00e8de un certain nombre d'arguments obligatoires et optionnels:\n\n- Obligatoire:\n - `query`: requ\u00eate lucene (cha\u00eene)\n- Facultatif: `filters`: requ\u00eate lucene (cha\u00eene):\n - `filters`: Requ\u00eates lucene additionnelles utilis\u00e9es pour filtrer les donn\u00e9es (liste de cha\u00eenes)\n - `fl`: Liste des champs \u00e0 retourner (cha\u00eene de champs s\u00e9par\u00e9s par des virgules)\n - `offset`: Offset auquel les \u00e9l\u00e9ments de la requ\u00eate doivent commencer (entier)\n - `rows`: Nombre d'enregistrements \u00e0 retourner (entier)\n - `sort`: Champ utilis\u00e9 pour le tri avec direction (cha\u00eene: ex. 'id desc')\n - `timeout`: Nombre maximum de millisecondes d'ex\u00e9cution de la requ\u00eate (entier)\n - `track_total_hits`: Nombre de hits \u00e0 suivre (par d\u00e9faut: 10k)\n\nVoici quelques exemples de requ\u00eates:\n\n```python\n# Rechercher tous les hits cr\u00e9\u00e9s par assemblyline, afficher les 50 premiers et ne renvoyer que leurs identifiants.\nhowler.search.hit(\"howler.analytic:assemblyline\", fl=\"howler.id\", rows=50)\n\n# Recherche de toutes les occurrences r\u00e9solues cr\u00e9\u00e9es au cours des cinq derniers jours, avec indication de leur identifiant et de l'analyste qui les a cr\u00e9\u00e9es. N'en afficher que dix, d\u00e9cal\u00e9s de 40\nhowler.search.hit(\"howler.status:resolved\", filters=['event.created:[now-5d TO now]'] fl=\"howler.id,howler.analytic\", rows=10, offset=40)\n\n# Recherche de tous les r\u00e9sultats, d\u00e9lai d'attente si la requ\u00eate prend plus de 100 ms\nhowler.search.hit(\"howler.id:*\", track_total_hits=100000000, timeout=100)\n```\n\n### Mise \u00e0 jour des r\u00e9sultats\n\nAfin de mettre \u00e0 jour les hits, il existe un certain nombre de fonctions support\u00e9es :\n\n- `howler.hit.update(...)`\n- `howler.hit.update_by_query(...)`\n- `howler.hit.overwrite(...)`\n\n#### `update()`\n\nSi vous souhaitez mettre \u00e0 jour un hit de mani\u00e8re transactionnelle, vous pouvez utiliser le code suivant:\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.update(hit_to_update[\"howler\"][\"id\"], [(UPDATE_SET, \"howler.score\", hit_to_update[\"howler\"][\"score\"] + 100)])\n```\n\nLes op\u00e9rations suivantes peuvent \u00eatre ex\u00e9cut\u00e9es pour mettre \u00e0 jour un r\u00e9sultat.\n\n**Op\u00e9rations de liste:**\n\n- `UPDATE_APPEND` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e\n- `UPDATE_APPEND_IF_MISSING` : Utilis\u00e9 pour ajouter une valeur \u00e0 une liste donn\u00e9e si la valeur n'est pas d\u00e9j\u00e0 dans la liste.\n- `UPDATE_REMOVE` : Supprime une valeur donn\u00e9e d'une liste\n\n**Op\u00e9rations num\u00e9riques:**\n\n- `UPDATE_DEC` : Diminue une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_INC` : Incr\u00e9mente une valeur num\u00e9rique de la quantit\u00e9 sp\u00e9cifi\u00e9e.\n- `UPDATE_MAX` : Fixe une valeur num\u00e9rique au maximum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n- `UPDATE_MIN` : Fixe une valeur num\u00e9rique au minimum de la valeur existante et de la valeur sp\u00e9cifi\u00e9e.\n\n**Op\u00e9rations polyvalentes:**\n\n- `UPDATE_SET` : Fixe la valeur d'un champ \u00e0 la valeur donn\u00e9e.\n- `UPDATE_DELETE` : Supprime la valeur d'un champ donn\u00e9\n\n#### `update_by_query()`\n\nCette fonction vous permet de mettre \u00e0 jour un grand nombre d'occurrences \u00e0 l'aide d'une requ\u00eate :\n\n```python\nclient.hit.update_by_query(f'howler.analytic : \"Exemple d\\'analytic\"', [(UPDATE_INC, \"howler.score\", 100)])\n```\n\nLes m\u00eames op\u00e9rations que dans `update()` peuvent \u00eatre utilis\u00e9es.\n\n### `overwrite()`\n\nCette fonction vous permet d'\u00e9craser directement un hit avec un objet hit partiel. C'est la plus facile \u00e0 utiliser, mais elle perd une partie de la validation et du traitement suppl\u00e9mentaire des fonctions de mise \u00e0 jour.\n\n```python\nhit_to_update = client.search.hit(\"howler.id:*\", rows=1, sort=\"event.created desc\")[\"items\"][0]\n\nresult = client.hit.overwrite(hit_to_update[\"howler\"][\"id\"], {\"source.ip\" : \"127.0.0.1\", \u201cdestination.ip\u201d : \"8.8.8.8\"})\n```\n"
|
|
@@ -1,32 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Clear, Code, Comment, DataObject, History, LinkSharp, OpenInNew, QueryStats } from '@mui/icons-material';
|
|
3
3
|
import { Badge, Box, Divider, IconButton, Skeleton, Stack, Tab, Tabs, Tooltip, useTheme } from '@mui/material';
|
|
4
4
|
import TuiIconButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomIconButton';
|
|
5
5
|
import { Icon } from '@iconify/react';
|
|
6
6
|
import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
|
|
7
|
-
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
8
7
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
8
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
9
9
|
import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
|
|
10
10
|
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
11
11
|
import VSBox from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBox';
|
|
12
12
|
import VSBoxContent from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxContent';
|
|
13
13
|
import VSBoxHeader from '@cccsaurora/howler-ui/components/elements/addons/layout/vsbox/VSBoxHeader';
|
|
14
14
|
import Phrase from '@cccsaurora/howler-ui/components/elements/addons/search/phrase/Phrase';
|
|
15
|
-
import BundleButton from '@cccsaurora/howler-ui/components/elements/display/icons/BundleButton';
|
|
16
15
|
import SocketBadge from '@cccsaurora/howler-ui/components/elements/display/icons/SocketBadge';
|
|
17
16
|
import JSONViewer from '@cccsaurora/howler-ui/components/elements/display/json/JSONViewer';
|
|
18
17
|
import HitActions from '@cccsaurora/howler-ui/components/elements/hit/HitActions';
|
|
19
18
|
import HitBanner from '@cccsaurora/howler-ui/components/elements/hit/HitBanner';
|
|
20
|
-
import HitComments from '@cccsaurora/howler-ui/components/elements/hit/HitComments';
|
|
21
|
-
import HitDetails from '@cccsaurora/howler-ui/components/elements/hit/HitDetails';
|
|
22
19
|
import HitLabels from '@cccsaurora/howler-ui/components/elements/hit/HitLabels';
|
|
23
20
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
24
21
|
import HitLinks from '@cccsaurora/howler-ui/components/elements/hit/HitLinks';
|
|
25
22
|
import HitOutline from '@cccsaurora/howler-ui/components/elements/hit/HitOutline';
|
|
26
23
|
import HitOverview from '@cccsaurora/howler-ui/components/elements/hit/HitOverview';
|
|
27
|
-
import HitRelated from '@cccsaurora/howler-ui/components/elements/hit/HitRelated';
|
|
28
24
|
import HitSummary from '@cccsaurora/howler-ui/components/elements/hit/HitSummary';
|
|
29
|
-
import
|
|
25
|
+
import ObjectDetails from '@cccsaurora/howler-ui/components/elements/ObjectDetails';
|
|
26
|
+
import RecordComments from '@cccsaurora/howler-ui/components/elements/record/RecordComments';
|
|
27
|
+
import RecordRelated from '@cccsaurora/howler-ui/components/elements/record/RecordRelated';
|
|
28
|
+
import RecordWorklog from '@cccsaurora/howler-ui/components/elements/record/RecordWorklog';
|
|
30
29
|
import useMyUserList from '@cccsaurora/howler-ui/components/hooks/useMyUserList';
|
|
31
30
|
import ErrorBoundary from '@cccsaurora/howler-ui/components/routes/ErrorBoundary';
|
|
32
31
|
import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
|
|
@@ -35,19 +34,20 @@ import { useTranslation } from 'react-i18next';
|
|
|
35
34
|
import { usePluginStore } from 'react-pluggable';
|
|
36
35
|
import { useLocation } from 'react-router-dom';
|
|
37
36
|
import { useContextSelector } from 'use-context-selector';
|
|
38
|
-
import { getUserList } from '@cccsaurora/howler-ui/utils/
|
|
37
|
+
import { getUserList } from '@cccsaurora/howler-ui/utils/recordFunctions';
|
|
39
38
|
import { validateRegex } from '@cccsaurora/howler-ui/utils/stringUtils';
|
|
39
|
+
import { isHit } from '@cccsaurora/howler-ui/utils/typeUtils';
|
|
40
40
|
import { tryParse } from '@cccsaurora/howler-ui/utils/utils';
|
|
41
41
|
import LeadRenderer from '../view/LeadRenderer';
|
|
42
|
-
const InformationPane = ({ onClose }) => {
|
|
42
|
+
const InformationPane = ({ onClose, selected: _selected }) => {
|
|
43
43
|
const { t, i18n } = useTranslation();
|
|
44
44
|
const theme = useTheme();
|
|
45
45
|
const location = useLocation();
|
|
46
|
-
const { emit,
|
|
46
|
+
const { emit, open } = useContext(SocketContext);
|
|
47
47
|
const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
|
|
48
|
-
const selected = useContextSelector(ParameterContext, ctx => ctx
|
|
48
|
+
const selected = useContextSelector(ParameterContext, ctx => ctx?.selected) ?? _selected;
|
|
49
49
|
const pluginStore = usePluginStore();
|
|
50
|
-
const
|
|
50
|
+
const getRecord = useContextSelector(RecordContext, ctx => ctx.getRecord);
|
|
51
51
|
const [userIds, setUserIds] = useState(new Set());
|
|
52
52
|
const [analytic, setAnalytic] = useState();
|
|
53
53
|
const [hasOverview, setHasOverview] = useState(false);
|
|
@@ -58,7 +58,7 @@ const InformationPane = ({ onClose }) => {
|
|
|
58
58
|
const [_dossiers, setDossiers] = useState(null);
|
|
59
59
|
const dossiers = useMemo(() => _dossiers ?? [], [_dossiers]);
|
|
60
60
|
const users = useMyUserList(userIds);
|
|
61
|
-
const
|
|
61
|
+
const record = useContextSelector(RecordContext, ctx => ctx.records[selected]);
|
|
62
62
|
howlerPluginStore.plugins.forEach(plugin => {
|
|
63
63
|
pluginStore.executeFunction(`${plugin}.on`, 'viewing');
|
|
64
64
|
});
|
|
@@ -66,14 +66,14 @@ const InformationPane = ({ onClose }) => {
|
|
|
66
66
|
if (!selected) {
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
-
if (!
|
|
69
|
+
if (!record?.howler.data) {
|
|
70
70
|
setLoading(true);
|
|
71
|
-
|
|
71
|
+
void getRecord(selected, true).finally(() => setLoading(false));
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
|
-
setUserIds(getUserList(
|
|
74
|
+
setUserIds(getUserList(record));
|
|
75
75
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
76
|
-
}, [
|
|
76
|
+
}, [getRecord, selected]);
|
|
77
77
|
useEffect(() => {
|
|
78
78
|
if (selected) {
|
|
79
79
|
setAnalytic(null);
|
|
@@ -82,25 +82,22 @@ const InformationPane = ({ onClose }) => {
|
|
|
82
82
|
}
|
|
83
83
|
}, [selected]);
|
|
84
84
|
useEffect(() => {
|
|
85
|
-
if (
|
|
86
|
-
getMatchingAnalytic(
|
|
85
|
+
if (isHit(record) && !analytic) {
|
|
86
|
+
void getMatchingAnalytic(record).then(setAnalytic);
|
|
87
87
|
}
|
|
88
|
-
}, [analytic, getMatchingAnalytic,
|
|
88
|
+
}, [analytic, getMatchingAnalytic, record]);
|
|
89
89
|
useEffect(() => {
|
|
90
|
-
if (
|
|
91
|
-
getMatchingDossiers(
|
|
90
|
+
if (isHit(record) && !_dossiers) {
|
|
91
|
+
void getMatchingDossiers(record).then(setDossiers);
|
|
92
92
|
}
|
|
93
|
-
}, [_dossiers, getMatchingDossiers,
|
|
93
|
+
}, [_dossiers, getMatchingDossiers, record]);
|
|
94
94
|
useEffect(() => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (tab === 'hit_aggregate' && !hit?.howler.is_bundle) {
|
|
99
|
-
setTab('overview');
|
|
95
|
+
if (isHit(record)) {
|
|
96
|
+
void getMatchingOverview(record).then(_overview => setHasOverview(!!_overview));
|
|
100
97
|
}
|
|
101
|
-
}, [
|
|
98
|
+
}, [getMatchingOverview, record]);
|
|
102
99
|
useEffect(() => {
|
|
103
|
-
if (selected &&
|
|
100
|
+
if (selected && open) {
|
|
104
101
|
emit({
|
|
105
102
|
broadcast: false,
|
|
106
103
|
action: 'viewing',
|
|
@@ -112,7 +109,7 @@ const InformationPane = ({ onClose }) => {
|
|
|
112
109
|
id: selected
|
|
113
110
|
});
|
|
114
111
|
}
|
|
115
|
-
}, [emit, selected,
|
|
112
|
+
}, [emit, selected, open]);
|
|
116
113
|
useEffect(() => {
|
|
117
114
|
if (hasOverview && tab === 'details') {
|
|
118
115
|
setTab('overview');
|
|
@@ -122,48 +119,37 @@ const InformationPane = ({ onClose }) => {
|
|
|
122
119
|
}
|
|
123
120
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
124
121
|
}, [hasOverview]);
|
|
125
|
-
/**
|
|
126
|
-
* What to show as the header? If loading a skeleton, then it depends on bundle or not. Bundles don't
|
|
127
|
-
* show anything while normal hits do
|
|
128
|
-
*/
|
|
129
|
-
const header = useMemo(() => {
|
|
130
|
-
if (loading && !hit?.howler?.is_bundle) {
|
|
131
|
-
return _jsx(Skeleton, { variant: "rounded", height: 152 });
|
|
132
|
-
}
|
|
133
|
-
else if (!!hit && !hit.howler.is_bundle) {
|
|
134
|
-
return _jsx(HitBanner, { layout: HitLayout.DENSE, hit: hit });
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
}, [hit, loading]);
|
|
140
122
|
const tabContent = useMemo(() => {
|
|
141
123
|
if (!tab) {
|
|
142
124
|
return;
|
|
143
125
|
}
|
|
126
|
+
const defaultContent = {
|
|
127
|
+
details: () => (_jsx(Box, { pr: 2, children: _jsx(ObjectDetails, { obj: record }) })),
|
|
128
|
+
comments: () => _jsx(RecordComments, { record: record, users: users }),
|
|
129
|
+
raw: () => _jsx(JSONViewer, { data: !loading && record, hideSearch: true, filter: filter }),
|
|
130
|
+
data: () => (_jsx(JSONViewer, { data: !loading && record?.howler?.data?.map(entry => tryParse(entry)), collapse: false, hideSearch: true, filter: filter })),
|
|
131
|
+
related: () => _jsx(RecordRelated, { record: record }),
|
|
132
|
+
worklog: () => _jsx(RecordWorklog, { record: !loading && record, users: users })
|
|
133
|
+
};
|
|
134
|
+
if (!isHit(record)) {
|
|
135
|
+
return defaultContent[tab]?.();
|
|
136
|
+
}
|
|
144
137
|
return {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
hit_comments: () => _jsx(HitComments, { hit: hit, users: users }),
|
|
148
|
-
hit_raw: () => _jsx(JSONViewer, { data: !loading && hit, hideSearch: true, filter: filter }),
|
|
149
|
-
hit_data: () => (_jsx(JSONViewer, { data: !loading && hit?.howler?.data?.map(entry => tryParse(entry)), collapse: false, hideSearch: true, filter: filter })),
|
|
150
|
-
hit_worklog: () => _jsx(HitWorklog, { hit: !loading && hit, users: users }),
|
|
138
|
+
...defaultContent,
|
|
139
|
+
overview: () => _jsx(HitOverview, { hit: record }),
|
|
151
140
|
hit_aggregate: () => _jsx(HitSummary, {}),
|
|
152
|
-
|
|
153
|
-
...Object.fromEntries((hit?.howler.dossier ?? []).map((lead, index) => [
|
|
141
|
+
...Object.fromEntries((record?.howler.dossier ?? []).map((lead, index) => [
|
|
154
142
|
'lead:' + index,
|
|
155
|
-
() => _jsx(LeadRenderer, { lead: lead, hit:
|
|
143
|
+
() => _jsx(LeadRenderer, { lead: lead, hit: record })
|
|
156
144
|
])),
|
|
157
145
|
...Object.fromEntries(dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => [
|
|
158
146
|
`external-lead:${dossierIndex}:${leadIndex}`,
|
|
159
|
-
() => _jsx(LeadRenderer, { lead: _lead, hit:
|
|
147
|
+
() => _jsx(LeadRenderer, { lead: _lead, hit: record })
|
|
160
148
|
])))
|
|
161
149
|
}[tab]?.();
|
|
162
|
-
}, [dossiers, filter,
|
|
150
|
+
}, [dossiers, filter, record, loading, tab, users]);
|
|
163
151
|
const hasError = useMemo(() => !validateRegex(filter), [filter]);
|
|
164
|
-
return (_jsxs(VSBox, { top: 10, sx: { height: '100%', flex: 1 }, children: [_jsxs(Stack, { direction: "column", flex: 1, sx: { overflowY: 'auto', flexGrow: 1 }, position: "relative", spacing: 1, ml: 2, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, flexShrink: 0, pr: 2,
|
|
165
|
-
!hit.howler.is_bundle &&
|
|
166
|
-
(!loading ? (_jsxs(_Fragment, { children: [_jsx(HitOutline, { hit: hit, layout: HitLayout.DENSE, forceAllFields: true }), _jsx(HitLabels, { hit: hit })] })) : (_jsx(Skeleton, { height: 124 }))), _jsx(HitLinks, { hit: hit, analytic: analytic, dossiers: dossiers }), _jsxs(VSBoxHeader, { ml: -1, mr: -1, pb: 1, sx: { top: '0px' }, children: [_jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: {
|
|
152
|
+
return (_jsxs(VSBox, { top: 10, sx: { height: '100%', flex: 1 }, children: [_jsxs(Stack, { direction: "column", flex: 1, sx: { overflowY: 'auto', flexGrow: 1 }, position: "relative", spacing: 1, ml: 2, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 0.5, flexShrink: 0, pr: 2, children: [_jsx(FlexOne, {}), onClose && !location.pathname.startsWith('/bundles') && (_jsx(TuiIconButton, { size: "small", onClick: onClose, tooltip: t('hit.panel.details.exit'), children: _jsx(Clear, {}) })), _jsx(SocketBadge, { size: "small" }), analytic && (_jsx(TuiIconButton, { size: "small", tooltip: t('analytic.open'), disabled: !analytic || loading, route: `/analytics/${analytic.analytic_id}`, children: _jsx(QueryStats, {}) })), !!record && (_jsx(TuiIconButton, { tooltip: t(`${record.__index}.open`), href: `/${record.__index}s/${selected}`, disabled: !record || loading, size: "small", target: "_blank", children: _jsx(OpenInNew, {}) }))] }), isHit(record) && (_jsxs(_Fragment, { children: [_jsx(Box, { pr: 2, children: _jsx(HitBanner, { layout: HitLayout.DENSE, hit: record }) }), !loading && (_jsxs(_Fragment, { children: [_jsx(HitOutline, { hit: record, layout: HitLayout.DENSE, forceAllFields: true }), _jsx(HitLabels, { hit: record })] })), _jsx(HitLinks, { hit: record, analytic: analytic, dossiers: dossiers })] })), loading && (_jsxs(_Fragment, { children: [_jsx(Skeleton, { variant: "rounded", height: 152 }), _jsx(Skeleton, { height: 124 })] })), _jsxs(VSBoxHeader, { ml: -1, mr: -1, pb: 1, sx: { top: '0px' }, children: [_jsxs(Tabs, { value: tab === 'overview' && !hasOverview ? 'details' : tab, sx: {
|
|
167
153
|
display: 'flex',
|
|
168
154
|
flexDirection: 'row',
|
|
169
155
|
pr: 2,
|
|
@@ -185,17 +171,18 @@ const InformationPane = ({ onClose }) => {
|
|
|
185
171
|
right: 0
|
|
186
172
|
}
|
|
187
173
|
}
|
|
188
|
-
}, variant: "scrollable", children: [_jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('
|
|
174
|
+
}, variant: "scrollable", children: [_jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('viewer.comments'), children: _jsx(Badge, { sx: {
|
|
189
175
|
'& > .MuiBadge-badge': {
|
|
190
176
|
backgroundColor: theme.palette.divider,
|
|
191
177
|
zIndex: 1,
|
|
192
178
|
right: theme.spacing(-0.5)
|
|
193
179
|
},
|
|
194
180
|
'& > svg': { zIndex: 2 }
|
|
195
|
-
}, badgeContent:
|
|
196
|
-
|
|
197
|
-
|
|
181
|
+
}, badgeContent: record?.howler.comment?.length ?? 0, children: _jsx(Comment, {}) }) }), value: "comments", onClick: () => setTab('comments') }), isHit(record) && hasOverview && (_jsx(Tab, { label: t('hit.viewer.overview'), value: "overview", onClick: () => setTab('overview') })), _jsx(Tab, { label: t('hit.viewer.details'), value: "details", onClick: () => setTab('details') }), isHit(record) &&
|
|
182
|
+
record?.howler.dossier?.map((lead, index) => (_jsx(Tab
|
|
183
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
184
|
+
, { label: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [lead.icon && _jsx(Icon, { icon: lead.icon }), _jsx("span", { children: i18n.language === 'en' ? lead.label.en : lead.label.fr })] }), value: 'lead:' + index, onClick: () => setTab('lead:' + index) }, 'lead:' + index))), dossiers.flatMap((_dossier, dossierIndex) => (_dossier.leads ?? []).map((_lead, leadIndex) => (_jsx(Tab
|
|
198
185
|
// eslint-disable-next-line react/no-array-index-key
|
|
199
|
-
, { label: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_lead.icon && _jsx(Icon, { icon: _lead.icon }), _jsx("span", { children: i18n.language === 'en' ? _lead.label.en : _lead.label.fr })] }), value: `external-lead:${dossierIndex}:${leadIndex}`, onClick: () => setTab(`external-lead:${dossierIndex}:${leadIndex}`) }, `external-lead:${dossierIndex}:${leadIndex}`)))), _jsx(FlexOne, {}), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.data'), children: _jsx(DataObject, {}) }), value: "
|
|
186
|
+
, { label: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_lead.icon && _jsx(Icon, { icon: _lead.icon }), _jsx("span", { children: i18n.language === 'en' ? _lead.label.en : _lead.label.fr })] }), value: `external-lead:${dossierIndex}:${leadIndex}`, onClick: () => setTab(`external-lead:${dossierIndex}:${leadIndex}`) }, `external-lead:${dossierIndex}:${leadIndex}`)))), _jsx(FlexOne, {}), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.data'), children: _jsx(DataObject, {}) }), value: "data", onClick: () => setTab('data'), disabled: !record?.howler?.data }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.json'), children: _jsx(Code, {}) }), value: "raw", onClick: () => setTab('raw') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.worklog'), children: _jsx(History, {}) }), value: "worklog", onClick: () => setTab('worklog') }), _jsx(Tab, { sx: { px: 2, minWidth: 0 }, label: _jsx(Tooltip, { title: t('hit.viewer.related'), children: _jsx(LinkSharp, {}) }), value: "related", onClick: () => setTab('related') })] }), ['raw', 'data'].includes(tab) && (_jsx(Phrase, { sx: { mt: 1, pr: 1 }, value: filter, onChange: setFilter, error: hasError, label: t('json.viewer.search.label'), placeholder: t('json.viewer.search.prompt'), endAdornment: _jsx(IconButton, { onClick: () => setFilter(''), children: _jsx(Clear, {}) }) }))] }), _jsx(ErrorBoundary, { children: _jsx(VSBoxContent, { mr: -1, ml: -1, height: "100%", children: _jsx(Stack, { height: "100%", flex: 1, children: tabContent }) }) })] }), isHit(record) && (_jsxs(Box, { pr: 2, bgcolor: theme.palette.background.default, position: "relative", children: [_jsx(Divider, { orientation: "horizontal" }), _jsx(HitActions, { hit: record })] }))] }));
|
|
200
187
|
};
|
|
201
188
|
export default InformationPane;
|
|
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
9
9
|
import { useContextSelector } from 'use-context-selector';
|
|
10
10
|
import HitFilter from './shared/HitFilter';
|
|
11
11
|
import HitSort from './shared/HitSort';
|
|
12
|
+
import IndexPicker from './shared/IndexPicker';
|
|
12
13
|
import SearchSpan from './shared/SearchSpan';
|
|
13
14
|
import ViewLink from './ViewLink';
|
|
14
15
|
const QuerySettings = ({ boxSx }) => {
|
|
@@ -25,6 +26,6 @@ const QuerySettings = ({ boxSx }) => {
|
|
|
25
26
|
await fetchViews();
|
|
26
27
|
addView('');
|
|
27
28
|
};
|
|
28
|
-
return (_jsx(Box, { sx: boxSx ?? { position: 'relative', maxWidth: '1200px' }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Grid, { container: true, spacing: 1, sx: theme => ({ ml: `${theme.spacing(-1)} !important`, mt: `${theme.spacing(-1)} !important` }), children: [_jsx(Grid, { item: true, children: _jsx(HitSort, {}) }), _jsx(Grid, { item: true, children: _jsx(SearchSpan, {}) }), currentViews?.map((view, id) => (_jsx(Grid, { item: true, children: _jsx(ViewLink, { id: id, viewId: view }) }, view))), filters?.map((filter, id) => (_jsx(Grid, { item: true, children: _jsx(HitFilter, { id: id, value: filter }) }, filter)))] }), _jsx(ChipPopper, { icon: _jsx(Add, {}), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, closeOnClick: true, slotProps: { chip: { size: 'small', color: 'primary' }, paper: { sx: { p: 1 } } }, children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Button, { id: "add-filter", "aria-label": t('hit.search.filter.add'), variant: "outlined", onClick: () => addFilter('howler.assessment:*'), disabled: filters?.some(filter => filter.endsWith('*')), sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.filter.add') })] }), _jsxs(Button, { id: "add-view", "aria-label": t('hit.search.view.add'), variant: "outlined", onClick: onAddView, disabled: !allowAddViews, sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.view.add') })] })] }) })] }) }));
|
|
29
|
+
return (_jsx(Box, { sx: boxSx ?? { position: 'relative', maxWidth: '1200px' }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Grid, { container: true, spacing: 1, sx: theme => ({ ml: `${theme.spacing(-1)} !important`, mt: `${theme.spacing(-1)} !important` }), children: [_jsx(Grid, { item: true, children: _jsx(IndexPicker, {}) }), _jsx(Grid, { item: true, children: _jsx(HitSort, {}) }), _jsx(Grid, { item: true, children: _jsx(SearchSpan, {}) }), currentViews?.map((view, id) => (_jsx(Grid, { item: true, children: _jsx(ViewLink, { id: id, viewId: view }) }, view))), filters?.map((filter, id) => (_jsx(Grid, { item: true, children: _jsx(HitFilter, { id: id, value: filter }) }, filter)))] }), _jsx(ChipPopper, { icon: _jsx(Add, {}), deleteIcon: _jsx(ArrowDropDown, {}), toggleOnDelete: true, closeOnClick: true, slotProps: { chip: { size: 'small', color: 'primary' }, paper: { sx: { p: 1 } } }, children: _jsxs(Stack, { spacing: 1, children: [_jsxs(Button, { id: "add-filter", "aria-label": t('hit.search.filter.add'), variant: "outlined", onClick: () => addFilter('howler.assessment:*'), disabled: filters?.some(filter => filter.endsWith('*')), sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.filter.add') })] }), _jsxs(Button, { id: "add-view", "aria-label": t('hit.search.view.add'), variant: "outlined", onClick: onAddView, disabled: !allowAddViews, sx: { display: 'flex', pl: 1 }, children: [_jsx(Add, { fontSize: "small", sx: { mr: 1 } }), _jsx("div", { style: { flex: 1 } }), _jsx("span", { children: t('hit.search.view.add') })] })] }) })] }) }));
|
|
29
30
|
};
|
|
30
31
|
export default memo(QuerySettings);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
3
|
import userEvent, {} from '@testing-library/user-event';
|
|
4
4
|
import { act } from 'react';
|
|
@@ -16,6 +16,9 @@ vi.mock('./shared/HitSort', () => ({
|
|
|
16
16
|
vi.mock('./shared/SearchSpan', () => ({
|
|
17
17
|
default: () => _jsx("div", { id: "search-span", children: "SearchSpan" })
|
|
18
18
|
}));
|
|
19
|
+
vi.mock('./shared/IndexPicker', () => ({
|
|
20
|
+
default: () => _jsx("div", { id: "index-picker", children: "IndexPicker" })
|
|
21
|
+
}));
|
|
19
22
|
vi.mock('./ViewLink', () => ({
|
|
20
23
|
default: ({ id, viewId }) => (_jsxs("div", { id: `view-link-${id}`, "data-view-id": viewId, children: ["ViewLink ", id, ": ", viewId] }))
|
|
21
24
|
}));
|
|
@@ -33,6 +36,7 @@ const mockFetchViews = vi.fn();
|
|
|
33
36
|
let mockParameterContext = {
|
|
34
37
|
filters: [],
|
|
35
38
|
views: [],
|
|
39
|
+
indexes: [],
|
|
36
40
|
addFilter: mockAddFilter,
|
|
37
41
|
addView: mockAddView
|
|
38
42
|
};
|
|
@@ -245,18 +249,15 @@ describe('QuerySettings', () => {
|
|
|
245
249
|
expect(screen.getByLabelText(i18n.t('hit.search.filter.add'))).toBeInTheDocument();
|
|
246
250
|
expect(screen.getByLabelText(i18n.t('hit.search.view.add'))).toBeInTheDocument();
|
|
247
251
|
});
|
|
248
|
-
it('should
|
|
252
|
+
it('should close after adding a filter', async () => {
|
|
249
253
|
render(_jsx(QuerySettings, {}), { wrapper: Wrapper });
|
|
250
254
|
const chipButton = screen.getByRole('button');
|
|
251
255
|
await user.click(chipButton);
|
|
252
256
|
const addFilterButton = screen.getByLabelText(i18n.t('hit.search.filter.add'));
|
|
253
|
-
await
|
|
254
|
-
|
|
255
|
-
await user.click(addFilterButton);
|
|
256
|
-
await user.click(addFilterButton);
|
|
257
|
-
});
|
|
258
|
-
expect(mockAddFilter).toHaveBeenCalledTimes(3);
|
|
257
|
+
await user.click(addFilterButton);
|
|
258
|
+
expect(mockAddFilter).toHaveBeenCalledTimes(1);
|
|
259
259
|
expect(mockAddFilter).toHaveBeenCalledWith('howler.assessment:*');
|
|
260
|
+
await waitFor(() => expect(screen.queryByLabelText(i18n.t('hit.search.filter.add'))).not.toBeInTheDocument());
|
|
260
261
|
});
|
|
261
262
|
});
|
|
262
263
|
describe('Grid Layout', () => {
|
|
@@ -270,16 +271,16 @@ describe('QuerySettings', () => {
|
|
|
270
271
|
mockParameterContext.views = ['view-1'];
|
|
271
272
|
const { container } = render(_jsx(QuerySettings, {}), { wrapper: Wrapper });
|
|
272
273
|
const gridItems = container.querySelectorAll('[class*="MuiGrid-item"]');
|
|
273
|
-
// HitSort + SearchSpan + 1 view + 2 filters = 5 items
|
|
274
|
-
expect(gridItems.length).toBe(
|
|
274
|
+
// IndexPicker + HitSort + SearchSpan + 1 view + 2 filters = 5 items
|
|
275
|
+
expect(gridItems.length).toBe(6);
|
|
275
276
|
});
|
|
276
277
|
it('should render correct number of items with multiple views', () => {
|
|
277
278
|
mockParameterContext.filters = ['filter1'];
|
|
278
279
|
mockParameterContext.views = ['view-1', 'view-2', 'view-3'];
|
|
279
280
|
const { container } = render(_jsx(QuerySettings, {}), { wrapper: Wrapper });
|
|
280
281
|
const gridItems = container.querySelectorAll('[class*="MuiGrid-item"]');
|
|
281
|
-
// HitSort + SearchSpan + 3 views + 1 filter = 6 items
|
|
282
|
-
expect(gridItems.length).toBe(
|
|
282
|
+
// IndexPicker + HitSort + SearchSpan + 3 views + 1 filter = 6 items
|
|
283
|
+
expect(gridItems.length).toBe(7);
|
|
283
284
|
});
|
|
284
285
|
});
|
|
285
286
|
describe('Edge Cases', () => {
|
|
@@ -508,11 +509,12 @@ describe('QuerySettings', () => {
|
|
|
508
509
|
mockParameterContext.views = ['view-1'];
|
|
509
510
|
const { container } = render(_jsx(QuerySettings, {}), { wrapper: Wrapper });
|
|
510
511
|
const gridItems = container.querySelectorAll('[class*="MuiGrid-item"]');
|
|
511
|
-
// Order: HitSort, SearchSpan, ViewLink(s), HitFilter(s)
|
|
512
|
-
expect(gridItems[0]).toContainElement(screen.getByTestId('
|
|
513
|
-
expect(gridItems[1]).toContainElement(screen.getByTestId('
|
|
514
|
-
expect(gridItems[2]).toContainElement(screen.getByTestId('
|
|
515
|
-
expect(gridItems[3]).toContainElement(screen.getByTestId('
|
|
512
|
+
// Order: IndexPicker, HitSort, SearchSpan, ViewLink(s), HitFilter(s)
|
|
513
|
+
expect(gridItems[0]).toContainElement(screen.getByTestId('index-picker'));
|
|
514
|
+
expect(gridItems[1]).toContainElement(screen.getByTestId('hit-sort'));
|
|
515
|
+
expect(gridItems[2]).toContainElement(screen.getByTestId('search-span'));
|
|
516
|
+
expect(gridItems[3]).toContainElement(screen.getByTestId('view-link-0'));
|
|
517
|
+
expect(gridItems[4]).toContainElement(screen.getByTestId('hit-filter-0'));
|
|
516
518
|
});
|
|
517
519
|
it('should pass correct props to ViewLink components', () => {
|
|
518
520
|
mockParameterContext.views = ['view-1', 'view-2'];
|