Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[frontend/backend] add filters based on schema definition in the Add entities panels #6086

Merged
merged 4 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { useFormatter } from '../../../../components/i18n';
import ContainerAddStixCoreObjectsLines, { containerAddStixCoreObjectsLinesQuery } from './ContainerAddStixCoreObjectsLines';
import StixDomainObjectCreation from '../stix_domain_objects/StixDomainObjectCreation';
import StixCyberObservableCreation from '../../observations/stix_cyber_observables/StixCyberObservableCreation';
import { UserContext } from '../../../../utils/hooks/useAuth';
import useAuth from '../../../../utils/hooks/useAuth';
import ListLines from '../../../../components/list_lines/ListLines';
import { constructHandleAddFilter, constructHandleRemoveFilter, emptyFilterGroup, filtersAfterSwitchLocalMode } from '../../../../utils/filters/filtersUtils';
import { emptyFilterGroup } from '../../../../utils/filters/filtersUtils';
import Drawer from '../drawer/Drawer';
import useAttributes from '../../../../utils/hooks/useAttributes';
import { usePaginationLocalStorage } from '../../../../utils/hooks/useLocalStorage';

const useStyles = makeStyles((theme) => ({
createButton: {
Expand Down Expand Up @@ -76,10 +77,11 @@ const ContainerAddStixCoreObjects = (props) => {
const [openSpeedDial, setOpenSpeedDial] = useState(false);
const [openCreateEntity, setOpenCreateEntity] = useState(false);
const [openCreateObservable, setOpenCreateObservable] = useState(false);
const [sortBy, setSortBy] = useState('_score');
const [orderAsc, setOrderAsc] = useState(false);

const { stixDomainObjectTypes, stixCyberObservableTypes } = useAttributes();
const {
platformModuleHelpers: { isRuntimeFieldEnable },
} = useAuth();

const targetEntityTypesFilterGroup = {
mode: 'and',
Expand All @@ -94,18 +96,71 @@ const ContainerAddStixCoreObjects = (props) => {
],
};

const [filters, setFilters] = useState(
targetStixCoreObjectTypes
&& !(targetStixCoreObjectTypes.includes('Stix-Domain-Object') || targetStixCoreObjectTypes.includes('Stix-Cyber-Observable'))
? targetEntityTypesFilterGroup
: emptyFilterGroup,
const isTypeDomainObject = (types) => {
return !types
Archidoit marked this conversation as resolved.
Show resolved Hide resolved
|| types.some((r) => stixDomainObjectTypes.indexOf(r) >= 0)
|| (types.length === 1 && types[0] === 'Stix-Domain-Object');
};
const isTypeObservable = (types) => {
return !types
Archidoit marked this conversation as resolved.
Show resolved Hide resolved
|| types.some((r) => stixCyberObservableTypes.indexOf(r) >= 0)
|| (types.length === 1 && types[0] === 'Stix-Cyber-Observable');
};

const resolveAvailableTypes = () => {
if (
targetStixCoreObjectTypes
&& isTypeDomainObject(targetStixCoreObjectTypes)
&& !isTypeObservable(targetStixCoreObjectTypes)
) {
return 'Stix-Domain-Object';
}
if (
targetStixCoreObjectTypes
&& isTypeObservable(targetStixCoreObjectTypes)
&& !isTypeDomainObject(targetStixCoreObjectTypes)
) {
return 'Stix-Cyber-Observable';
}
if (
!targetStixCoreObjectTypes
|| (isTypeObservable(targetStixCoreObjectTypes)
&& isTypeDomainObject(targetStixCoreObjectTypes))
) {
return 'Stix-Core-Object';
}
return null;
};

const LOCAL_STORAGE_KEY = `container-${containerId}-add-objects`;
const { viewStorage, helpers, paginationOptions: addObjectsPaginationOptions } = usePaginationLocalStorage(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the goal of the hook? The filters should be saved in local storage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But not only the filters but there are some other utilities.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we are not saving it in local storage here? Or at least we are not fetching what is in local storage when loading the page as if I refresh I don't see the previous filters I made

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are correct. I'm unsure about the intention here. The saving in localStorage is functional, but upon closing the drawer, we reset the state and clear all filters.
https://github.com/OpenCTI-Platform/opencti/pull/6086/files/eb6c9427dcab8747dfda8053e566206e15ad277a#diff-c501c533b095ec5c7f5b484df27e33d102e40b4369f12d850a58a82b465ea31bR419-R421

I believe we're utilizing the useLocalStoragePagination for the infinite loader rather than for storing the filter in localStorage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this history, we are using here a simple filter state. If we can confirm the behavior, I can change the code and use "useFilterState" instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See with the product. We will keep the state. I remove the resetState function

LOCAL_STORAGE_KEY,
{
searchTerm: '',
sortBy: '_score',
orderAsc: false,
filters: targetStixCoreObjectTypes
&& !(targetStixCoreObjectTypes.includes('Stix-Domain-Object') || targetStixCoreObjectTypes.includes('Stix-Cyber-Observable'))
? targetEntityTypesFilterGroup
: emptyFilterGroup,
types: [resolveAvailableTypes()],
numberOfElements: {
number: 0,
symbol: '',
},
},
);
const [numberOfElements, setNumberOfElements] = useState({
number: 0,
symbol: '',
});
const [searchTerm, setSearchTerm] = useState('');

const {
sortBy,
orderAsc,
searchTerm,
filters,
numberOfElements,
} = viewStorage;

const containerRef = useRef(null);
const keyword = mapping && searchTerm.length === 0 ? selectedText : searchTerm;
const handleOpenCreateEntity = () => {
setOpenCreateEntity(true);
setOpenSpeedDial(false);
Expand All @@ -122,56 +177,21 @@ const ContainerAddStixCoreObjects = (props) => {
setOpenCreateObservable(false);
setOpenSpeedDial(false);
};
const handleSort = (field, sortOrderAsc) => {
setSortBy(field);
setOrderAsc(sortOrderAsc);
};
const handleAddFilter = (key, id, op = 'eq', event = null) => {
if (event) {
event.stopPropagation();
event.preventDefault();
}
setFilters(constructHandleAddFilter(filters, key, id, op));
};
const handleRemoveFilter = (key, op = 'eq') => {
setFilters(constructHandleRemoveFilter(filters, key, op));
};

const handleSwitchLocalMode = (localFilter) => {
setFilters(filtersAfterSwitchLocalMode(filters, localFilter));
};

const handleSwitchGlobalMode = () => {
if (filters) {
setFilters({
...filters,
mode: filters.mode === 'and' ? 'or' : 'and',
});
}
};
const isTypeDomainObject = (types) => {
return !types || types.some((r) => stixDomainObjectTypes.indexOf(r) >= 0);
};
const isTypeObservable = (types) => {
return (
!types || types.some((r) => stixCyberObservableTypes.indexOf(r) >= 0)
);
};
const renderDomainObjectCreation = (searchPaginationOptions) => {
return (
<StixDomainObjectCreation
display={open}
inputValue={mapping && searchTerm.length === 0 ? selectedText : searchTerm}
inputValue={keyword}
paginationKey="Pagination_stixCoreObjects"
paginationOptions={searchPaginationOptions}
confidence={confidence}
defaultCreatedBy={defaultCreatedBy}
defaultMarkingDefinitions={defaultMarkingDefinitions}
stixDomainObjectTypes={
targetStixCoreObjectTypes && targetStixCoreObjectTypes.length > 0
? targetStixCoreObjectTypes
: []
}
targetStixCoreObjectTypes && targetStixCoreObjectTypes.length > 0
? targetStixCoreObjectTypes
: []
}
/>
);
};
Expand All @@ -180,7 +200,7 @@ const ContainerAddStixCoreObjects = (props) => {
<StixCyberObservableCreation
display={open}
contextual={true}
inputValue={mapping && searchTerm.length === 0 ? selectedText : searchTerm}
inputValue={keyword}
paginationKey="Pagination_stixCoreObjects"
paginationOptions={searchPaginationOptions}
defaultCreatedBy={defaultCreatedBy}
Expand Down Expand Up @@ -223,25 +243,25 @@ const ContainerAddStixCoreObjects = (props) => {
</SpeedDial>
<StixDomainObjectCreation
display={open}
inputValue={mapping && searchTerm.length === 0 ? selectedText : searchTerm}
inputValue={keyword}
paginationKey="Pagination_stixCoreObjects"
paginationOptions={searchPaginationOptions}
confidence={confidence}
defaultCreatedBy={defaultCreatedBy}
defaultMarkingDefinitions={defaultMarkingDefinitions}
stixCoreObjectTypes={
targetStixCoreObjectTypes && targetStixCoreObjectTypes.length > 0
? targetStixCoreObjectTypes
: []
}
targetStixCoreObjectTypes && targetStixCoreObjectTypes.length > 0
? targetStixCoreObjectTypes
: []
}
speeddial={true}
open={openCreateEntity}
handleClose={() => handleCloseCreateEntity()}
/>
<StixCyberObservableCreation
display={open}
contextual={true}
inputValue={mapping && searchTerm.length === 0 ? selectedText : searchTerm}
inputValue={keyword}
paginationKey="Pagination_stixCoreObjects"
paginationOptions={searchPaginationOptions}
defaultCreatedBy={defaultCreatedBy}
Expand All @@ -253,30 +273,6 @@ const ContainerAddStixCoreObjects = (props) => {
</>
);
};
const resolveAvailableTypes = () => {
if (
targetStixCoreObjectTypes
&& isTypeDomainObject(targetStixCoreObjectTypes)
&& !isTypeObservable(targetStixCoreObjectTypes)
) {
return 'Stix-Domain-Object';
}
if (
targetStixCoreObjectTypes
&& isTypeObservable(targetStixCoreObjectTypes)
&& !isTypeDomainObject(targetStixCoreObjectTypes)
) {
return 'Stix-Cyber-Observable';
}
if (
!targetStixCoreObjectTypes
|| (isTypeObservable(targetStixCoreObjectTypes)
&& isTypeDomainObject(targetStixCoreObjectTypes))
) {
return 'Stix-Core-Object';
}
return null;
};
const renderEntityCreation = (searchPaginationOptions) => {
if (
targetStixCoreObjectTypes
Expand All @@ -301,8 +297,7 @@ const ContainerAddStixCoreObjects = (props) => {
}
return null;
};
const buildColumns = (platformModuleHelpers) => {
const isRuntimeSort = platformModuleHelpers.isRuntimeFieldEnable();
const buildColumns = () => {
return {
entity_type: {
label: 'Type',
Expand All @@ -317,7 +312,7 @@ const ContainerAddStixCoreObjects = (props) => {
createdBy: {
label: 'Author',
width: '15%',
isSortable: isRuntimeSort,
isSortable: isRuntimeFieldEnable(),
},
objectLabel: {
label: 'Labels',
Expand All @@ -327,65 +322,60 @@ const ContainerAddStixCoreObjects = (props) => {
objectMarking: {
label: 'Marking',
width: '15%',
isSortable: isRuntimeSort,
isSortable: isRuntimeFieldEnable(),
},
};
};
const renderSearchResults = (searchPaginationOptions) => {
return (
<UserContext.Consumer>
{({ platformModuleHelpers }) => (
<ListLines
sortBy={sortBy}
orderAsc={orderAsc}
dataColumns={buildColumns(platformModuleHelpers)}
handleSearch={setSearchTerm}
keyword={mapping && searchTerm.length === 0 ? selectedText : searchTerm}
handleSort={handleSort}
handleAddFilter={handleAddFilter}
handleRemoveFilter={handleRemoveFilter}
handleSwitchLocalMode={handleSwitchLocalMode}
handleSwitchGlobalMode={handleSwitchGlobalMode}
disableCards={true}
filters={filters}
paginationOptions={searchPaginationOptions}
numberOfElements={numberOfElements}
iconExtension={true}
parametersWithPadding={true}
disableExport={true}
availableEntityTypes={[resolveAvailableTypes()]}
>
<QueryRenderer
query={containerAddStixCoreObjectsLinesQuery}
variables={{ count: 100, ...searchPaginationOptions }}
render={({ props: renderProps }) => (
<ContainerAddStixCoreObjectsLines
data={renderProps}
containerId={containerId}
paginationOptions={paginationOptions}
dataColumns={buildColumns(platformModuleHelpers)}
initialLoading={renderProps === null}
knowledgeGraph={knowledgeGraph}
containerStixCoreObjects={containerStixCoreObjects}
onAdd={onAdd}
onDelete={onDelete}
setNumberOfElements={setNumberOfElements}
mapping={mapping}
containerRef={containerRef}
/>
)}
<ListLines
helpers={helpers}
sortBy={sortBy}
orderAsc={orderAsc}
dataColumns={buildColumns()}
handleSearch={helpers.handleSearch}
keyword={keyword}
handleSort={helpers.handleSort}
handleAddFilter={helpers.handleAddFilter}
handleRemoveFilter={helpers.handleRemoveFilter}
handleSwitchLocalMode={helpers.handleSwitchLocalMode}
handleSwitchGlobalMode={helpers.handleSwitchGlobalMode}
disableCards={true}
filters={filters}
paginationOptions={searchPaginationOptions}
numberOfElements={numberOfElements}
iconExtension={true}
parametersWithPadding={true}
disableExport={true}
availableEntityTypes={targetStixCoreObjectTypes}
entityTypes={targetStixCoreObjectTypes}
>
<QueryRenderer
query={containerAddStixCoreObjectsLinesQuery}
variables={{ count: 100, ...searchPaginationOptions }}
render={({ props: renderProps }) => (
<ContainerAddStixCoreObjectsLines
data={renderProps}
containerId={containerId}
paginationOptions={paginationOptions}
dataColumns={buildColumns()}
initialLoading={renderProps === null}
knowledgeGraph={knowledgeGraph}
containerStixCoreObjects={containerStixCoreObjects}
onAdd={onAdd}
onDelete={onDelete}
setNumberOfElements={helpers.handleSetNumberOfElements}
mapping={mapping}
containerRef={containerRef}
/>
</ListLines>
)}
</UserContext.Consumer>
)}
/>
</ListLines>
);
};
const searchPaginationOptions = {
types: [resolveAvailableTypes()],
search: mapping && searchTerm.length === 0 ? selectedText : searchTerm,
filters,
orderBy: sortBy,
orderMode: orderAsc ? 'asc' : 'desc',
...addObjectsPaginationOptions,
search: keyword,
};
const renderButton = () => {
if (knowledgeGraph) {
Expand Down Expand Up @@ -426,22 +416,12 @@ const ContainerAddStixCoreObjects = (props) => {
</Fab>
);
};
const resetState = () => {
setSearchTerm('');
setFilters(
targetStixCoreObjectTypes
&& !(targetStixCoreObjectTypes.includes('Stix-Domain-Object') || targetStixCoreObjectTypes.includes('Stix-Cyber-Observable'))
? targetEntityTypesFilterGroup
: emptyFilterGroup,
);
};
return (
<>
{!mapping && renderButton()}
<Drawer
open={mapping ? openDrawer : open}
onClose={() => {
resetState();
if (mapping) {
handleClose();
} else {
Expand Down
Loading
Loading