Projects STRLCPY opencti Commits 36b893eb
🤬
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/components/Theme.d.ts
    1  -import { PaletteOptions, TypeBackground } from '@mui/material/styles/createPalette';
    2  -import { ThemeOptions } from '@mui/material/styles/createTheme';
     1 +import { PaletteColorOptions, PaletteOptions, TypeBackground } from '@mui/material/styles/createPalette';
     2 +import { Theme as MuiTheme, ThemeOptions } from '@mui/material/styles/createTheme';
     3 + 
     4 +interface ExtendedColor extends PaletteColorOptions {
     5 + main: string
     6 +}
    3 7   
    4 8  interface ExtendedBackground extends TypeBackground {
    5  - nav: string,
    6  - accent: string,
    7  - shadow: string,
     9 + nav: string
     10 + accent: string
     11 + shadow: string
    8 12  }
    9 13   
    10 14  interface ExtendedPaletteOptions extends PaletteOptions {
    11  - background: Partial<ExtendedBackground>;
     15 + background: Partial<ExtendedBackground>
     16 + primary: Partial<ExtendedColor>
    12 17  }
    13 18   
    14 19  interface ExtendedThemeOptions extends ThemeOptions {
    15  - logo: string | null,
    16  - palette: ExtendedPaletteOptions;
     20 + logo: string | null
     21 + palette: ExtendedPaletteOptions
     22 +}
     23 + 
     24 +export interface Theme extends MuiTheme {
     25 + palette: ExtendedPaletteOptions
    17 26  }
    18 27   
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/components/list_lines/ListLinesContent.js
    skipped 118 lines
    119 119   if (!edge) {
    120 120   return (
    121 121   <div key={key} style={style}>
    122  - {React.cloneElement(DummyLineComponent, {
     122 + {/* TODO remove this when all components are pure function without compose() */}
     123 + {!React.isValidElement(DummyLineComponent) ? (
     124 + <DummyLineComponent dataColumns={dataColumns} />
     125 + ) : React.cloneElement(DummyLineComponent, {
    123 126   dataColumns,
    124 127   })}
    125 128   </div>
    skipped 2 lines
    128 131   const { node, types } = edge;
    129 132   return (
    130 133   <div key={key} style={style}>
    131  - {React.cloneElement(LineComponent, {
    132  - dataColumns,
    133  - node,
    134  - types,
    135  - paginationOptions,
    136  - entityId,
    137  - entityLink,
    138  - refetch,
    139  - me,
    140  - onLabelClick,
    141  - selectedElements,
    142  - deSelectedElements,
    143  - selectAll,
    144  - onToggleEntity,
    145  - connectionKey,
    146  - isTo,
    147  - })}
     134 + {/* TODO remove this when all components are pure function without compose() */}
     135 + {!React.isValidElement(LineComponent) ? (
     136 + <LineComponent
     137 + dataColumns={dataColumns}
     138 + node={node}
     139 + types={types}
     140 + paginationOptions={paginationOptions}
     141 + entityId={entityId}
     142 + entityLink={entityLink}
     143 + refetch={refetch}
     144 + me={me}
     145 + onLabelClick={onLabelClick}
     146 + selectedElements={selectedElements}
     147 + deSelectedElements={deSelectedElements}
     148 + selectAll={selectAll}
     149 + onToggleEntity={onToggleEntity}
     150 + connectionKey={connectionKey}
     151 + isTo={isTo}
     152 + />
     153 + ) : (
     154 + React.cloneElement(LineComponent, {
     155 + dataColumns,
     156 + node,
     157 + types,
     158 + paginationOptions,
     159 + entityId,
     160 + entityLink,
     161 + refetch,
     162 + me,
     163 + onLabelClick,
     164 + selectedElements,
     165 + deSelectedElements,
     166 + selectAll,
     167 + onToggleEntity,
     168 + connectionKey,
     169 + isTo,
     170 + }))}
    148 171   </div>
    149 172   );
    150 173   }
    skipped 11 lines
    162 185   ? dataList.length + this.state.loadingRowCount
    163 186   : dataList.length;
    164 187   const rowCount = initialLoading ? nbOfRowsToLoad : countWithLoading;
     188 + console.log('Row count ?', isLoading(), dataList.length, this.state.loadingRowCount, countWithLoading);
    165 189   return (
    166 190   <WindowScroller ref={this._setRef} scrollElement={window}>
    167 191   {({ height, isScrolling, onChildScroll, scrollTop }) => (
    skipped 67 lines
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/components/list_lines/index.d.ts opencti-platform/opencti-front/src/components/list_lines/index.ts
    1 1  import { FunctionComponent } from 'react';
    2 2   
    3 3  export interface DataColumn { isSortable: boolean, label: string, width?: string | number }
     4 +export type DataColumns = Record<string, DataColumn>;
    4 5   
    5 6  export type Filters<F = Record<string, unknown>[]> = Record<string, F>;
    6 7   
    7 8  export enum OrderMode {
    8  - 'asc',
    9  - 'desc',
     9 + asc = 'asc',
     10 + desc = 'desc',
    10 11  }
    11 12   
    12 13  export interface PaginationOptions {
    skipped 10 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/analysis/reports/ReportCreation.js
    skipped 16 lines
    17 17  import { commitMutation, QueryRenderer } from '../../../../relay/environment';
    18 18  import TextField from '../../../../components/TextField';
    19 19  import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    20  -import { attributesQuery } from '../../settings/attributes/AttributesLines';
    21 20  import Loader from '../../../../components/Loader';
    22 21  import ObjectLabelField from '../../common/form/ObjectLabelField';
    23 22  import CreatedByField from '../../common/form/CreatedByField';
    skipped 5 lines
    29 28  import AutocompleteFreeSoloField from '../../../../components/AutocompleteFreeSoloField';
    30 29  import Security, { SETTINGS_SETLABELS } from '../../../../utils/Security';
    31 30  import DateTimePickerField from '../../../../components/DateTimePickerField';
     31 +import { vocabulariesQuery } from "../../settings/attributes/VocabulariesLines";
    32 32   
    33 33  const styles = (theme) => ({
    34 34   drawerPaper: {
    skipped 154 lines
    189 189   onClose={this.handleClose.bind(this)}
    190 190   >
    191 191   <QueryRenderer
    192  - query={attributesQuery}
    193  - variables={{ key: 'report_types' }}
     192 + query={vocabulariesQuery}
     193 + variables={{ category: 'report_types' }}
    194 194   render={({ props }) => {
    195 195   if (props && props.runtimeAttributes) {
    196 196   const reportEdges = props.runtimeAttributes.edges.map(
    skipped 210 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/analysis/reports/ReportEditionOverview.js
    skipped 9 lines
    10 10  import inject18n from '../../../../components/i18n';
    11 11  import TextField from '../../../../components/TextField';
    12 12  import { SubscriptionFocus } from '../../../../components/Subscription';
    13  -import { attributesQuery } from '../../settings/attributes/AttributesLines';
    14 13  import Loader from '../../../../components/Loader';
    15 14  import CreatedByField from '../../common/form/CreatedByField';
    16 15  import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    skipped 12 lines
    29 28   convertStatus,
    30 29  } from '../../../../utils/Edition';
    31 30  import DateTimePickerField from '../../../../components/DateTimePickerField';
     31 +import { vocabulariesQuery } from "../../settings/attributes/VocabulariesLines";
    32 32   
    33 33  const styles = (theme) => ({
    34 34   createButton: {
    skipped 243 lines
    278 278   return (
    279 279   <div>
    280 280   <QueryRenderer
    281  - query={attributesQuery}
    282  - variables={{ key: 'report_types' }}
     281 + query={vocabulariesQuery}
     282 + variables={{ category: 'report_types' }}
    283 283   render={({ props }) => {
    284 284   if (props && props.runtimeAttributes) {
    285 285   const reportEdges = props.runtimeAttributes.edges.map(
    skipped 272 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelCreation.js
    skipped 23 lines
    24 24  import ObjectMarkingField from '../../common/form/ObjectMarkingField';
    25 25  import MarkDownField from '../../../../components/MarkDownField';
    26 26  import ExternalReferencesField from '../../common/form/ExternalReferencesField';
    27  -import { attributesQuery } from '../../settings/attributes/AttributesLines';
    28 27  import Loader from '../../../../components/Loader';
    29 28  import Security, { SETTINGS_SETLABELS } from '../../../../utils/Security';
    30 29  import AutocompleteField from '../../../../components/AutocompleteField';
    31 30  import ItemIcon from '../../../../components/ItemIcon';
    32 31  import AutocompleteFreeSoloField from '../../../../components/AutocompleteFreeSoloField';
     32 +import { vocabulariesQuery } from "../../settings/attributes/VocabulariesLines";
    33 33   
    34 34  const styles = (theme) => ({
    35 35   drawerPaper: {
    skipped 154 lines
    190 190   onClose={this.handleClose.bind(this)}
    191 191   >
    192 192   <QueryRenderer
    193  - query={attributesQuery}
    194  - variables={{ key: 'channel_types' }}
     193 + query={vocabulariesQuery}
     194 + variables={{ category: 'channel_types' }}
    195 195   render={({ props }) => {
    196  - if (props && props.runtimeAttributes) {
    197  - const channelEdges = props.runtimeAttributes.edges.map(
    198  - (e) => e.node.value,
     196 + if (props && props.vocabularies) {
     197 + const channelEdges = props.vocabularies.edges.map(
     198 + (e) => e.node.name,
    199 199   );
    200 200   const elements = R.uniq([
    201 201   ...channelEdges,
    skipped 204 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelEditionOverview.js
    skipped 30 lines
    31 31  } from '../../../../utils/Edition';
    32 32  import { QueryRenderer, commitMutation } from '../../../../relay/environment';
    33 33  import Security, { SETTINGS_SETLABELS } from '../../../../utils/Security';
    34  -import { attributesQuery } from '../../settings/attributes/AttributesLines';
    35 34  import Loader from '../../../../components/Loader';
    36 35  import AutocompleteField from '../../../../components/AutocompleteField';
    37 36  import ItemIcon from '../../../../components/ItemIcon';
    38 37  import AutocompleteFreeSoloField from '../../../../components/AutocompleteFreeSoloField';
     38 +import { vocabulariesQuery } from "../../settings/attributes/VocabulariesLines";
    39 39   
    40 40  const styles = (theme) => ({
    41 41   createButton: {
    skipped 226 lines
    268 268   )(channel);
    269 269   return (
    270 270   <QueryRenderer
    271  - query={attributesQuery}
    272  - variables={{ key: 'channel_types' }}
     271 + query={vocabulariesQuery}
     272 + variables={{ category: 'channel_types' }}
    273 273   render={({ props }) => {
    274 274   if (props && props.runtimeAttributes) {
    275 275   const channelEdges = props.runtimeAttributes.edges.map(
    skipped 233 lines
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/arsenal/channels/ChannelsLines.js
    skipped 19 lines
    20 20   
    21 21   render() {
    22 22   const { initialLoading, dataColumns, relay, onLabelClick } = this.props;
     23 + console.log("Coucou ", typeof <ChannelLine />);
    23 24   return (
    24 25   <ListLinesContent
    25 26   initialLoading={initialLoading}
    skipped 114 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/nav/TopMenuSettings.js
    skipped 121 lines
    122 122   <Security needs={[SETTINGS_SETLABELS]}>
    123 123   <Button
    124 124   component={Link}
    125  - to="/dashboard/settings/attributes"
     125 + to="/dashboard/settings/vocabularies"
    126 126   variant={
    127  - location.pathname.includes('/dashboard/settings/attributes')
     127 + location.pathname.includes('/dashboard/settings/vocabularies')
    128 128   ? 'contained'
    129 129   : 'text'
    130 130   }
    131 131   size="small"
    132 132   color={
    133  - location.pathname.includes('/dashboard/settings/attributes')
     133 + location.pathname.includes('/dashboard/settings/vocabularies')
    134 134   ? 'secondary'
    135 135   : 'primary'
    136 136   }
    skipped 21 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/Attributes.tsx
    1  -import React from 'react';
    2  -import { useParams } from 'react-router-dom';
    3  -import makeStyles from '@mui/styles/makeStyles';
    4  -import { QueryRenderer } from '../../../relay/environment';
    5  -import ListLines from '../../../components/list_lines/ListLines';
    6  -import LabelsAttributesMenu from './LabelsAttributesMenu';
    7  -import AttributesLines, { AttributesLinesProps, attributesLinesQuery } from './attributes/AttributesLines';
    8  -import LabelCreation from './labels/LabelCreation';
    9  -import useLocalStorage, { LocalStorage, localStorageToPaginationOptions } from '../../../utils/hooks/useLocalStorage';
    10  - 
    11  -const useStyles = makeStyles(() => ({
    12  - container: {
    13  - margin: 0,
    14  - padding: '0 200px 50px 0',
    15  - },
    16  -}));
    17  - 
    18  -const Attributes = () => {
    19  - const classes = useStyles();
    20  - const { attributeKey: attributeName } = useParams();
    21  - const [viewStorage, setViewStorage] = useLocalStorage('view-attributes', {
    22  - sortBy: 'value',
    23  - orderAsc: true,
    24  - searchTerm: '',
    25  - });
    26  - 
    27  - const handleSearch = (value: string) => setViewStorage((c) => ({ ...c, searchTerm: value }));
    28  - 
    29  - const handleSort = (field: string, order: boolean) => setViewStorage((c) => ({
    30  - ...c,
    31  - sortBy: field,
    32  - orderAsc: order,
    33  - }));
    34  - 
    35  - const renderLines = (paginationOptions: LocalStorage) => {
    36  - const { sortBy, orderAsc, searchTerm } = paginationOptions;
    37  - 
    38  - const dataColumns = {
    39  - value: {
    40  - label: 'Value',
    41  - width: '80%',
    42  - isSortable: true,
    43  - },
    44  - };
    45  - return (
    46  - <ListLines
    47  - sortBy={sortBy}
    48  - orderAsc={orderAsc}
    49  - dataColumns={dataColumns}
    50  - handleSort={handleSort}
    51  - handleSearch={handleSearch}
    52  - displayImport={false}
    53  - secondaryAction={true}
    54  - keyword={searchTerm}
    55  - >
    56  - <QueryRenderer
    57  - query={attributesLinesQuery}
    58  - variables={localStorageToPaginationOptions(paginationOptions)}
    59  - render={({ props }: { props: AttributesLinesProps['data'] }) => (
    60  - <AttributesLines
    61  - data={props}
    62  - paginationOptions={paginationOptions}
    63  - dataColumns={dataColumns}
    64  - initialLoading={!props}
    65  - />
    66  - )}
    67  - />
    68  - </ListLines>
    69  - );
    70  - };
    71  - 
    72  - const queryProps = { ...viewStorage, attributeName };
    73  - 
    74  - return (
    75  - <div className={classes.container}>
    76  - <LabelsAttributesMenu />
    77  - {renderLines(queryProps)}
    78  - <LabelCreation paginationOptions={viewStorage} />
    79  - </div>
    80  - );
    81  -};
    82  - 
    83  -export default Attributes;
    84  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/KillChainPhases.js
    skipped 14 lines
    15 15   killChainPhasesLinesQuery,
    16 16  } from './kill_chain_phases/KillChainPhasesLines';
    17 17  import KillChainPhaseCreation from './kill_chain_phases/KillChainPhaseCreation';
    18  -import LabelsAttributesMenu from './LabelsAttributesMenu';
     18 +import LabelsVocabulariesMenu from './LabelsVocabulariesMenu';
    19 19   
    20 20  const styles = () => ({
    21 21   container: {
    skipped 111 lines
    133 133   };
    134 134   return (
    135 135   <div className={classes.container}>
    136  - <LabelsAttributesMenu />
     136 + <LabelsVocabulariesMenu />
    137 137   {view === 'lines' ? this.renderLines(paginationOptions) : ''}
    138 138   <KillChainPhaseCreation paginationOptions={paginationOptions} />
    139 139   </div>
    skipped 17 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/Labels.js
    skipped 11 lines
    12 12  import ListLines from '../../../components/list_lines/ListLines';
    13 13  import LabelsLines, { labelsLinesQuery } from './labels/LabelsLines';
    14 14  import LabelCreation from './labels/LabelCreation';
    15  -import LabelsAttributesMenu from './LabelsAttributesMenu';
     15 +import LabelsVocabulariesMenu from './LabelsVocabulariesMenu';
    16 16   
    17 17  const styles = () => ({
    18 18   container: {
    skipped 91 lines
    110 110   };
    111 111   return (
    112 112   <div className={classes.container}>
    113  - <LabelsAttributesMenu />
     113 + <LabelsVocabulariesMenu />
    114 114   {view === 'lines' ? this.renderLines(paginationOptions) : ''}
    115 115   <LabelCreation paginationOptions={paginationOptions} />
    116 116   </div>
    skipped 13 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/LabelsAttributesMenu.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { withRouter, Link } from 'react-router-dom';
    4  -import { compose } from 'ramda';
    5  -import withStyles from '@mui/styles/withStyles';
    6  -import Drawer from '@mui/material/Drawer';
    7  -import MenuList from '@mui/material/MenuList';
    8  -import MenuItem from '@mui/material/MenuItem';
    9  -import ListItemText from '@mui/material/ListItemText';
    10  -import inject18n from '../../../components/i18n';
    11  - 
    12  -const styles = (theme) => ({
    13  - drawer: {
    14  - minHeight: '100vh',
    15  - width: 200,
    16  - position: 'fixed',
    17  - overflow: 'auto',
    18  - padding: 0,
    19  - backgroundColor: theme.palette.background.navLight,
    20  - },
    21  - toolbar: theme.mixins.toolbar,
    22  -});
    23  - 
    24  -class LabelsAttributesMenu extends Component {
    25  - render() {
    26  - const { t, location, classes } = this.props;
    27  - return (
    28  - <Drawer
    29  - variant="permanent"
    30  - anchor="right"
    31  - classes={{ paper: classes.drawer }}
    32  - >
    33  - <div className={classes.toolbar} />
    34  - <MenuList component="nav">
    35  - <MenuItem
    36  - component={Link}
    37  - to={'/dashboard/settings/attributes/labels'}
    38  - selected={
    39  - location.pathname === '/dashboard/settings/attributes/labels'
    40  - }
    41  - dense={false}
    42  - >
    43  - <ListItemText primary={t('Labels')} />
    44  - </MenuItem>
    45  - <MenuItem
    46  - component={Link}
    47  - to={'/dashboard/settings/attributes/kill_chain_phases'}
    48  - selected={
    49  - location.pathname
    50  - === '/dashboard/settings/attributes/kill_chain_phases'
    51  - }
    52  - dense={false}
    53  - >
    54  - <ListItemText primary={t('Kill chain phases')} />
    55  - </MenuItem>
    56  - <MenuItem
    57  - component={Link}
    58  - to={'/dashboard/settings/attributes/fields/report_types'}
    59  - selected={
    60  - location.pathname
    61  - === '/dashboard/settings/attributes/fields/report_types'
    62  - }
    63  - dense={false}
    64  - >
    65  - <ListItemText primary={t('Report types')} />
    66  - </MenuItem>
    67  - </MenuList>
    68  - </Drawer>
    69  - );
    70  - }
    71  -}
    72  - 
    73  -LabelsAttributesMenu.propTypes = {
    74  - classes: PropTypes.object,
    75  - location: PropTypes.object,
    76  - t: PropTypes.func,
    77  -};
    78  - 
    79  -export default compose(
    80  - inject18n,
    81  - withRouter,
    82  - withStyles(styles),
    83  -)(LabelsAttributesMenu);
    84  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/LabelsVocabulariesMenu.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { Link, useLocation } from 'react-router-dom';
     3 +import Drawer from '@mui/material/Drawer';
     4 +import MenuList from '@mui/material/MenuList';
     5 +import MenuItem from '@mui/material/MenuItem';
     6 +import ListItemText from '@mui/material/ListItemText';
     7 +import makeStyles from '@mui/styles/makeStyles';
     8 +import { graphql, useLazyLoadQuery } from 'react-relay';
     9 +import { useFormatter } from '../../../components/i18n';
     10 +import { Theme } from '../../../components/Theme';
     11 +import { LabelsVocabulariesMenuQuery } from './__generated__/LabelsVocabulariesMenuQuery.graphql';
     12 + 
     13 +const useStyles = makeStyles<Theme>((theme) => ({
     14 + drawer: {
     15 + minHeight: '100vh',
     16 + width: 200,
     17 + position: 'fixed',
     18 + overflow: 'auto',
     19 + padding: 0,
     20 + backgroundColor: theme.palette.background.nav,
     21 + },
     22 + toolbar: theme.mixins.toolbar,
     23 +}));
     24 + 
     25 +const vocabCategoriesQuery = graphql`
     26 + query LabelsVocabulariesMenuQuery {
     27 + vocabularyCategories
     28 + }
     29 +`;
     30 + 
     31 +const LabelsVocabulariesMenu: FunctionComponent = () => {
     32 + const location = useLocation();
     33 + const { t } = useFormatter();
     34 + const classes = useStyles();
     35 + 
     36 + const data = useLazyLoadQuery<LabelsVocabulariesMenuQuery>(vocabCategoriesQuery, {});
     37 + 
     38 + return (
     39 + <Drawer
     40 + variant="permanent"
     41 + anchor="right"
     42 + classes={{ paper: classes.drawer }}
     43 + >
     44 + <div className={classes.toolbar} />
     45 + <MenuList component="nav">
     46 + <MenuItem
     47 + component={Link}
     48 + to={'/dashboard/settings/vocabularies/labels'}
     49 + selected={
     50 + location.pathname === '/dashboard/settings/vocabularies/labels'
     51 + }
     52 + dense={false}
     53 + >
     54 + <ListItemText primary={t('Labels')} />
     55 + </MenuItem>
     56 + <MenuItem
     57 + component={Link}
     58 + to={'/dashboard/settings/vocabularies/kill_chain_phases'}
     59 + selected={
     60 + location.pathname
     61 + === '/dashboard/settings/vocabularies/kill_chain_phases'
     62 + }
     63 + dense={false}
     64 + >
     65 + <ListItemText primary={t('Kill chain phases')} />
     66 + </MenuItem>
     67 + {data.vocabularyCategories.map((cat) => (
     68 + <MenuItem
     69 + component={Link}
     70 + to={`/dashboard/settings/vocabularies/fields/${cat}`}
     71 + selected={
     72 + location.pathname
     73 + === `/dashboard/settings/vocabularies/fields/${cat}`
     74 + }
     75 + dense={false}
     76 + >
     77 + <ListItemText primary={t(cat)} />
     78 + </MenuItem>
     79 + ))}
     80 + </MenuList>
     81 + </Drawer>
     82 + );
     83 +};
     84 + 
     85 +export default LabelsVocabulariesMenu;
     86 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/Root.js
    skipped 8 lines
    9 9  import MarkingDefinitions from './MarkingDefinitions';
    10 10  import Rules from './Rules';
    11 11  import KillChainPhases from './KillChainPhases';
    12  -import Attributes from './Attributes';
    13 12  import Labels from './Labels';
    14 13  import Workflow from './Workflow';
    15 14  import Retention from './Retention';
    16 15  import { BoundaryRoute } from '../Error';
    17 16  import Security, { SETTINGS } from '../../../utils/Security';
     17 +import Vocabulary from "./Vocabulary";
    18 18   
    19 19  const Root = () => (
    20 20   <Switch>
    skipped 46 lines
    67 67   <BoundaryRoute exact path="/dashboard/settings/rules" component={Rules} />
    68 68   <BoundaryRoute
    69 69   exact
    70  - path="/dashboard/settings/attributes"
    71  - render={() => <Redirect to="/dashboard/settings/attributes/labels" />}
     70 + path="/dashboard/settings/vocabularies"
     71 + render={() => <Redirect to="/dashboard/settings/vocabularies/labels" />}
    72 72   />
    73 73   <BoundaryRoute
    74 74   exact
    75  - path="/dashboard/settings/attributes/labels"
     75 + path="/dashboard/settings/vocabularies/labels"
    76 76   component={Labels}
    77 77   />
    78 78   <BoundaryRoute
    79 79   exact
    80  - path="/dashboard/settings/attributes/kill_chain_phases"
     80 + path="/dashboard/settings/vocabularies/kill_chain_phases"
    81 81   component={KillChainPhases}
    82 82   />
    83 83   <BoundaryRoute
    84 84   exact
    85  - path="/dashboard/settings/attributes/fields/:attributeKey"
    86  - component={Attributes}
     85 + path="/dashboard/settings/vocabularies/fields/:category"
     86 + component={Vocabulary}
    87 87   />
    88 88   </Security>
    89 89   </Switch>
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/Vocabulary.tsx
     1 +import React from 'react';
     2 +import { useParams } from 'react-router-dom';
     3 +import makeStyles from '@mui/styles/makeStyles';
     4 +import { QueryRenderer } from '../../../relay/environment';
     5 +import ListLines from '../../../components/list_lines/ListLines';
     6 +import useLocalStorage, { localStorageToPaginationOptions } from '../../../utils/hooks/useLocalStorage';
     7 +import VocabulariesLines, { VocabulariesLinesProps, vocabulariesLinesQuery } from './attributes/VocabulariesLines';
     8 +import LabelsVocabulariesMenu from './LabelsVocabulariesMenu';
     9 +import VocabularyCreation from './attributes/VocabularyCreation';
     10 + 
     11 +const useStyles = makeStyles(() => ({
     12 + container: {
     13 + margin: 0,
     14 + padding: '0 200px 50px 0',
     15 + },
     16 +}));
     17 + 
     18 +const Vocabulary = () => {
     19 + const classes = useStyles();
     20 + const { category } = useParams();
     21 + const [viewStorage, setViewStorage] = useLocalStorage(`view-vocabulary-${category}`, {
     22 + sortBy: 'name',
     23 + orderAsc: true,
     24 + searchTerm: '',
     25 + // Needed as the defaultValue doesn't work but breaks the connection :(
     26 + // count: 200,
     27 + });
     28 + 
     29 + const handleSearch = (value: string) => setViewStorage((c) => ({ ...c, searchTerm: value }));
     30 + 
     31 + const handleSort = (field: string, order: boolean) => setViewStorage((c) => ({
     32 + ...c,
     33 + sortBy: field,
     34 + orderAsc: order,
     35 + }));
     36 + 
     37 + const queryProps = localStorageToPaginationOptions({ ...viewStorage, category });
     38 + 
     39 + const renderLines = () => {
     40 + const dataColumns = {
     41 + name: {
     42 + label: 'Name',
     43 + width: '40%',
     44 + isSortable: true,
     45 + },
     46 + label: {
     47 + label: 'Label',
     48 + width: '60%',
     49 + isSortable: true,
     50 + },
     51 + };
     52 + console.log('Query props', queryProps);
     53 + return (
     54 + <ListLines
     55 + sortBy={queryProps.orderBy}
     56 + orderAsc={queryProps.orderMode}
     57 + dataColumns={dataColumns}
     58 + handleSort={handleSort}
     59 + handleSearch={handleSearch}
     60 + displayImport={false}
     61 + secondaryAction={true}
     62 + keyword={queryProps.search}
     63 + >
     64 + <QueryRenderer
     65 + query={vocabulariesLinesQuery}
     66 + variables={queryProps}
     67 + render={({ props }: { props: VocabulariesLinesProps['data'] }) => (
     68 + <VocabulariesLines
     69 + data={props}
     70 + paginationOptions={queryProps}
     71 + dataColumns={dataColumns}
     72 + initialLoading={!props}
     73 + />
     74 + )}
     75 + />
     76 + </ListLines>
     77 + );
     78 + };
     79 + 
     80 + return (
     81 + <div className={classes.container}>
     82 + <LabelsVocabulariesMenu />
     83 + {renderLines()}
     84 + {category && <VocabularyCreation category={category} paginationOptions={queryProps} />}
     85 + </div>
     86 + );
     87 +};
     88 + 
     89 +export default Vocabulary;
     90 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/attributes/AttributeLine.js
    1  -import React, { Component } from 'react';
    2  -import * as PropTypes from 'prop-types';
    3  -import { graphql, createFragmentContainer } from 'react-relay';
    4  -import withStyles from '@mui/styles/withStyles';
    5  -import ListItem from '@mui/material/ListItem';
    6  -import ListItemIcon from '@mui/material/ListItemIcon';
    7  -import ListItemText from '@mui/material/ListItemText';
    8  -import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
    9  -import { MoreVertOutlined, ShortTextOutlined } from '@mui/icons-material';
    10  -import { compose } from 'ramda';
    11  -import Skeleton from '@mui/material/Skeleton';
    12  -import inject18n from '../../../../components/i18n';
    13  -import AttributePopover from './AttributePopover';
    14  - 
    15  -const styles = (theme) => ({
    16  - item: {
    17  - paddingLeft: 10,
    18  - height: 50,
    19  - cursor: 'default',
    20  - },
    21  - itemIcon: {
    22  - color: theme.palette.primary.main,
    23  - },
    24  - bodyItem: {
    25  - height: 20,
    26  - fontSize: 13,
    27  - float: 'left',
    28  - whiteSpace: 'nowrap',
    29  - overflow: 'hidden',
    30  - textOverflow: 'ellipsis',
    31  - paddingRight: 5,
    32  - },
    33  - goIcon: {
    34  - position: 'absolute',
    35  - right: -10,
    36  - },
    37  - itemIconDisabled: {
    38  - color: theme.palette.grey[700],
    39  - },
    40  - placeholder: {
    41  - display: 'inline-block',
    42  - height: '1em',
    43  - backgroundColor: theme.palette.grey[700],
    44  - },
    45  -});
    46  - 
    47  -const AttributeLineComponent = (props) => {
    48  - const { classes, node, dataColumns, paginationOptions, refetch } = props;
    49  - return (
    50  - <ListItem classes={{ root: classes.item }} divider={true} button={true}>
    51  - <ListItemIcon classes={{ root: classes.itemIcon }}>
    52  - <ShortTextOutlined />
    53  - </ListItemIcon>
    54  - <ListItemText
    55  - primary={
    56  - <div>
    57  - <div
    58  - className={classes.bodyItem}
    59  - style={{ width: dataColumns.value.width }}
    60  - >
    61  - {node.value}
    62  - </div>
    63  - </div>
    64  - }
    65  - />
    66  - <ListItemSecondaryAction>
    67  - <AttributePopover
    68  - attribute={node}
    69  - refetch={refetch}
    70  - paginationOptions={paginationOptions}
    71  - />
    72  - </ListItemSecondaryAction>
    73  - </ListItem>
    74  - );
    75  -};
    76  - 
    77  -AttributeLineComponent.propTypes = {
    78  - dataColumns: PropTypes.object,
    79  - node: PropTypes.object,
    80  - paginationOptions: PropTypes.object,
    81  - me: PropTypes.object,
    82  - classes: PropTypes.object,
    83  -};
    84  - 
    85  -const AttributeLineFragment = createFragmentContainer(AttributeLineComponent, {
    86  - node: graphql`
    87  - fragment AttributeLine_node on Attribute {
    88  - id
    89  - key
    90  - value
    91  - }
    92  - `,
    93  -});
    94  - 
    95  -export const AttributeLine = compose(
    96  - inject18n,
    97  - withStyles(styles),
    98  -)(AttributeLineFragment);
    99  - 
    100  -class AttributeLineDummyComponent extends Component {
    101  - render() {
    102  - const { classes, dataColumns } = this.props;
    103  - return (
    104  - <ListItem classes={{ root: classes.item }} divider={true}>
    105  - <ListItemIcon classes={{ root: classes.itemIconDisabled }}>
    106  - <Skeleton
    107  - animation="wave"
    108  - variant="circular"
    109  - width={30}
    110  - height={30}
    111  - />
    112  - </ListItemIcon>
    113  - <ListItemText
    114  - primary={
    115  - <div>
    116  - <div
    117  - className={classes.bodyItem}
    118  - style={{ width: dataColumns.value.width }}
    119  - >
    120  - <Skeleton
    121  - animation="wave"
    122  - variant="rectangular"
    123  - width="90%"
    124  - height={20}
    125  - />
    126  - </div>
    127  - </div>
    128  - }
    129  - />
    130  - <ListItemSecondaryAction classes={{ root: classes.itemIconDisabled }}>
    131  - <MoreVertOutlined />
    132  - </ListItemSecondaryAction>
    133  - </ListItem>
    134  - );
    135  - }
    136  -}
    137  - 
    138  -AttributeLineDummyComponent.propTypes = {
    139  - dataColumns: PropTypes.object,
    140  - classes: PropTypes.object,
    141  -};
    142  - 
    143  -export const AttributeLineDummy = compose(
    144  - inject18n,
    145  - withStyles(styles),
    146  -)(AttributeLineDummyComponent);
    147  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/attributes/AttributeLine.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { createFragmentContainer, graphql } from 'react-relay';
     3 +import ListItem from '@mui/material/ListItem';
     4 +import ListItemIcon from '@mui/material/ListItemIcon';
     5 +import ListItemText from '@mui/material/ListItemText';
     6 +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction';
     7 +import { MoreVertOutlined, ShortTextOutlined } from '@mui/icons-material';
     8 +import Skeleton from '@mui/material/Skeleton';
     9 +import makeStyles from '@mui/styles/makeStyles';
     10 +import AttributePopover from './AttributePopover';
     11 +import { Theme } from '../../../../components/Theme';
     12 +import { DataColumns } from '../../../../components/list_lines';
     13 +import { LocalStorage } from '../../../../utils/hooks/useLocalStorage';
     14 +import { AttributeLine_node$data } from './__generated__/AttributeLine_node.graphql';
     15 + 
     16 +const useStyles = makeStyles<Theme>((theme) => ({
     17 + item: {
     18 + paddingLeft: 10,
     19 + height: 50,
     20 + cursor: 'default',
     21 + },
     22 + itemIcon: {
     23 + color: theme.palette.primary?.main,
     24 + },
     25 + bodyItem: {
     26 + height: 20,
     27 + fontSize: 13,
     28 + float: 'left',
     29 + whiteSpace: 'nowrap',
     30 + overflow: 'hidden',
     31 + textOverflow: 'ellipsis',
     32 + paddingRight: 5,
     33 + },
     34 + goIcon: {
     35 + position: 'absolute',
     36 + right: -10,
     37 + },
     38 + itemIconDisabled: {
     39 + color: theme.palette.grey?.[700],
     40 + },
     41 + placeholder: {
     42 + display: 'inline-block',
     43 + height: '1em',
     44 + backgroundColor: theme.palette.grey?.[700],
     45 + },
     46 +}));
     47 + 
     48 +interface AttributeLineProps {
     49 + node: Record<string, string>,
     50 + dataColumns: DataColumns,
     51 + paginationOptions: LocalStorage,
     52 + refetch: () => void,
     53 +}
     54 + 
     55 +const AttributeLineComponent: FunctionComponent<AttributeLineProps> = ({ node, dataColumns, paginationOptions, refetch }) => {
     56 + const classes = useStyles();
     57 + return (
     58 + <ListItem classes={{ root: classes.item }} divider={true} button={true}>
     59 + <ListItemIcon classes={{ root: classes.itemIcon }}>
     60 + <ShortTextOutlined />
     61 + </ListItemIcon>
     62 + {Object.entries(dataColumns).map(([key, value]) => (
     63 + <ListItemText
     64 + key={key}
     65 + primary={
     66 + <div>
     67 + <div
     68 + className={classes.bodyItem}
     69 + style={{ width: value.width }}
     70 + >
     71 + {node[key]}
     72 + </div>
     73 + </div>
     74 + }
     75 + />
     76 + ))}
     77 + <ListItemSecondaryAction>
     78 + <AttributePopover
     79 + attribute={node}
     80 + refetch={refetch}
     81 + paginationOptions={paginationOptions}
     82 + />
     83 + </ListItemSecondaryAction>
     84 + </ListItem>
     85 + );
     86 +};
     87 + 
     88 +export const AttributeLine = createFragmentContainer(AttributeLineComponent, {
     89 + node: graphql`
     90 + fragment AttributeLine_node on Vocabulary {
     91 + id
     92 + name
     93 + label
     94 + }
     95 + `,
     96 +});
     97 + 
     98 +export const AttributeLineDummy = ({ dataColumns }: { dataColumns: DataColumns }) => {
     99 + const classes = useStyles();
     100 + return (
     101 + <ListItem classes={{ root: classes.item }} divider={true}>
     102 + <ListItemIcon classes={{ root: classes.itemIconDisabled }}>
     103 + <Skeleton
     104 + animation="wave"
     105 + variant="circular"
     106 + width={30}
     107 + height={30}
     108 + />
     109 + </ListItemIcon>
     110 + {Object.entries(dataColumns).map(([key, value]) => (
     111 + <ListItemText
     112 + key={key}
     113 + primary={
     114 + <div>
     115 + <div
     116 + className={classes.bodyItem}
     117 + style={{ width: value.width }}
     118 + >
     119 + <Skeleton
     120 + animation="wave"
     121 + variant="rectangular"
     122 + width={value.width}
     123 + height={20}
     124 + />
     125 + </div>
     126 + </div>
     127 + }
     128 + />
     129 + ))}
     130 + <ListItemSecondaryAction classes={{ root: classes.itemIconDisabled }}>
     131 + <MoreVertOutlined />
     132 + </ListItemSecondaryAction>
     133 + </ListItem>
     134 + );
     135 +};
     136 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/attributes/AttributesLines.tsx
    1  -import React, { FunctionComponent } from 'react';
    2  -import { createPaginationContainer, graphql } from 'react-relay';
    3  -import { RelayPaginationProp } from 'react-relay/ReactRelayTypes';
    4  -import ListLinesContent from '../../../../components/list_lines/ListLinesContent';
    5  -import { AttributeLine, AttributeLineDummy } from './AttributeLine';
    6  -import { AttributesLinesAttributesQuery$data } from './__generated__/AttributesLinesAttributesQuery.graphql';
    7  -import { DataColumn } from '../../../../components/list_lines';
    8  -import { LocalStorage } from '../../../../utils/hooks/useLocalStorage';
    9  - 
    10  -export interface AttributesLinesProps {
    11  - paginationOptions: LocalStorage,
    12  - dataColumns: Record<string, DataColumn>,
    13  - data?: AttributesLinesAttributesQuery$data,
    14  - relay: RelayPaginationProp,
    15  - initialLoading?: boolean,
    16  -}
    17  - 
    18  -const nbOfRowsToLoad = 200;
    19  - 
    20  -const AttributesLines: FunctionComponent<AttributesLinesProps> = ({ data, initialLoading, dataColumns, relay, paginationOptions }) => {
    21  - const attributes = data?.runtimeAttributes?.edges ?? [];
    22  - const globalCount = data?.runtimeAttributes?.pageInfo?.globalCount ?? nbOfRowsToLoad;
    23  - const refetch = () => relay.refetchConnection(nbOfRowsToLoad);
    24  - return (
    25  - <ListLinesContent
    26  - initialLoading={initialLoading}
    27  - loadMore={relay.loadMore}
    28  - hasMore={relay.hasMore}
    29  - refetch={refetch}
    30  - isLoading={relay.isLoading}
    31  - dataList={attributes}
    32  - globalCount={globalCount}
    33  - LineComponent={<AttributeLine />}
    34  - DummyLineComponent={<AttributeLineDummy />}
    35  - dataColumns={dataColumns}
    36  - nbOfRowsToLoad={nbOfRowsToLoad}
    37  - paginationOptions={paginationOptions}
    38  - />
    39  - );
    40  -};
    41  - 
    42  -export const attributesQuery = graphql`
    43  - query AttributesLinesAttributesQuery($key: String!) {
    44  - runtimeAttributes(attributeName: $key) {
    45  - edges {
    46  - node {
    47  - id
    48  - key
    49  - value
    50  - }
    51  - }
    52  - pageInfo {
    53  - endCursor
    54  - hasNextPage
    55  - globalCount
    56  - }
    57  - }
    58  - }
    59  -`;
    60  - 
    61  -export const attributesLinesQuery = graphql`
    62  - query AttributesLinesPaginationQuery(
    63  - $attributeName: String!
    64  - $search: String
    65  - $count: Int
    66  - $orderMode: OrderingMode
    67  - ) {
    68  - ...AttributesLines_data
    69  - @arguments(
    70  - attributeName: $attributeName
    71  - search: $search
    72  - count: $count
    73  - orderMode: $orderMode
    74  - )
    75  - }
    76  -`;
    77  - 
    78  -export default createPaginationContainer(
    79  - AttributesLines,
    80  - {
    81  - data: graphql`
    82  - fragment AttributesLines_data on Query
    83  - @argumentDefinitions(
    84  - attributeName: { type: "String!" }
    85  - search: { type: "String" }
    86  - count: { type: "Int", defaultValue: 200 }
    87  - orderMode: { type: "OrderingMode", defaultValue: asc }
    88  - ) {
    89  - runtimeAttributes(
    90  - attributeName: $attributeName
    91  - search: $search
    92  - first: $count
    93  - orderMode: $orderMode
    94  - ) @connection(key: "Pagination_runtimeAttributes") {
    95  - edges {
    96  - node {
    97  - ...AttributeLine_node
    98  - }
    99  - }
    100  - pageInfo {
    101  - endCursor
    102  - hasNextPage
    103  - globalCount
    104  - }
    105  - }
    106  - }
    107  - `,
    108  - },
    109  - {
    110  - direction: 'forward',
    111  - getConnectionFromProps(props) {
    112  - return props.data && props.data.runtimeAttributes;
    113  - },
    114  - getFragmentVariables(prevVars, totalCount) {
    115  - return {
    116  - ...prevVars,
    117  - count: totalCount,
    118  - };
    119  - },
    120  - getVariables(props, { count }, fragmentVariables) {
    121  - return {
    122  - count,
    123  - attributeName: fragmentVariables.attributeName,
    124  - search: fragmentVariables.search,
    125  - orderMode: fragmentVariables.orderMode,
    126  - };
    127  - },
    128  - query: attributesLinesQuery,
    129  - },
    130  -);
    131  - 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/attributes/VocabulariesLines.tsx
     1 +import React, { FunctionComponent } from 'react';
     2 +import { createPaginationContainer, graphql } from 'react-relay';
     3 +import { RelayPaginationProp } from 'react-relay/ReactRelayTypes';
     4 +import ListLinesContent from '../../../../components/list_lines/ListLinesContent';
     5 +import { AttributeLine, AttributeLineDummy } from './AttributeLine';
     6 +import { DataColumns } from '../../../../components/list_lines';
     7 +import { LocalStorage } from '../../../../utils/hooks/useLocalStorage';
     8 +import { VocabulariesLinesQuery$data } from './__generated__/VocabulariesLinesQuery.graphql';
     9 + 
     10 +export interface VocabulariesLinesProps {
     11 + paginationOptions: LocalStorage,
     12 + dataColumns: DataColumns,
     13 + data?: VocabulariesLinesQuery$data,
     14 + relay: RelayPaginationProp,
     15 + initialLoading?: boolean,
     16 +}
     17 + 
     18 +const nbOfRowsToLoad = 200;
     19 + 
     20 +const VocabulariesLines: FunctionComponent<VocabulariesLinesProps> = ({ data, initialLoading, dataColumns, relay, paginationOptions }) => {
     21 + const vocabularies = data?.vocabularies?.edges ?? [];
     22 + const globalCount = data?.vocabularies?.pageInfo?.globalCount ?? nbOfRowsToLoad;
     23 + const refetch = () => relay.refetchConnection(nbOfRowsToLoad);
     24 + console.log('vocabularies', vocabularies);
     25 + return (
     26 + <ListLinesContent
     27 + initialLoading={initialLoading}
     28 + loadMore={relay.loadMore}
     29 + hasMore={relay.hasMore}
     30 + refetch={refetch}
     31 + isLoading={relay.isLoading}
     32 + dataList={vocabularies}
     33 + globalCount={globalCount}
     34 + LineComponent={AttributeLine}
     35 + DummyLineComponent={AttributeLineDummy}
     36 + dataColumns={dataColumns}
     37 + nbOfRowsToLoad={nbOfRowsToLoad}
     38 + paginationOptions={paginationOptions}
     39 + />
     40 + );
     41 +};
     42 + 
     43 +export const vocabulariesQuery = graphql`
     44 + query VocabulariesLinesQuery($category: VocabularyCategory!) {
     45 + vocabularies(category: $category) {
     46 + edges {
     47 + node {
     48 + id
     49 + name
     50 + label
     51 + }
     52 + }
     53 + pageInfo {
     54 + endCursor
     55 + hasNextPage
     56 + globalCount
     57 + }
     58 + }
     59 + }
     60 +`;
     61 + 
     62 +export const vocabulariesLinesQuery = graphql`
     63 + query VocabulariesLinesPaginationQuery(
     64 + $category: VocabularyCategory!
     65 + $search: String
     66 + $count: Int
     67 + $orderMode: OrderingMode
     68 + $orderBy: VocabularyOrdering
     69 + ) {
     70 + ...VocabulariesLines_data
     71 + @arguments(
     72 + category: $category
     73 + search: $search
     74 + count: $count
     75 + orderMode: $orderMode
     76 + orderBy: $orderBy
     77 + )
     78 + }
     79 +`;
     80 + 
     81 +export default createPaginationContainer(
     82 + VocabulariesLines,
     83 + {
     84 + data: graphql`
     85 + fragment VocabulariesLines_data on Query
     86 + @argumentDefinitions(
     87 + category: { type: "VocabularyCategory!" }
     88 + search: { type: "String" }
     89 + count: { type: "Int" }
     90 + orderMode: { type: "OrderingMode", defaultValue: asc }
     91 + orderBy: { type: "VocabularyOrdering", defaultValue: name }
     92 + ) {
     93 + vocabularies(
     94 + category: $category
     95 + search: $search
     96 + first: $count
     97 + orderMode: $orderMode
     98 + orderBy: $orderBy
     99 + ) @connection(key: "Pagination_vocabularies") {
     100 + edges {
     101 + node {
     102 + ...AttributeLine_node
     103 + }
     104 + }
     105 + pageInfo {
     106 + endCursor
     107 + hasNextPage
     108 + globalCount
     109 + }
     110 + }
     111 + }
     112 + `,
     113 + },
     114 + {
     115 + direction: 'forward',
     116 + getConnectionFromProps(props) {
     117 + return props.data && props.data.vocabularies;
     118 + },
     119 + getFragmentVariables(prevVars, totalCount) {
     120 + return {
     121 + ...prevVars,
     122 + count: totalCount,
     123 + };
     124 + },
     125 + getVariables(props, { count }, fragmentVariables) {
     126 + return {
     127 + count,
     128 + category: fragmentVariables.category,
     129 + search: fragmentVariables.search,
     130 + orderMode: fragmentVariables.orderMode,
     131 + };
     132 + },
     133 + query: vocabulariesLinesQuery,
     134 + },
     135 +);
     136 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/attributes/VocabularyCreation.tsx
     1 +import Fab from '@mui/material/Fab';
     2 +import { Add, Close } from '@mui/icons-material';
     3 +import React, { FunctionComponent, useState } from 'react';
     4 +import makeStyles from '@mui/styles/makeStyles';
     5 +import IconButton from '@mui/material/IconButton';
     6 +import Typography from '@mui/material/Typography';
     7 +import { Field, Form, Formik, FormikConfig } from 'formik';
     8 +import Button from '@mui/material/Button';
     9 +import Drawer from '@mui/material/Drawer';
     10 +import { graphql, useMutation } from 'react-relay';
     11 +import * as Yup from 'yup';
     12 +import TextField from '../../../../components/TextField';
     13 +import type { Theme } from '../../../../components/Theme';
     14 +import { useFormatter } from '../../../../components/i18n';
     15 +import { VocabularyCreationMutation } from './__generated__/VocabularyCreationMutation.graphql';
     16 +import { insertNode } from '../../../../utils/Store';
     17 +import type { LocalStorage } from '../../../../utils/hooks/useLocalStorage';
     18 + 
     19 +interface VocabularyCreationProps {
     20 + category: string,
     21 + paginationOptions: LocalStorage,
     22 +}
     23 + 
     24 +const useStyles = makeStyles<Theme>((theme) => ({
     25 + drawerPaper: {
     26 + minHeight: '100vh',
     27 + width: '50%',
     28 + position: 'fixed',
     29 + transition: theme.transitions.create('width', {
     30 + easing: theme.transitions.easing.sharp,
     31 + duration: theme.transitions.duration.enteringScreen,
     32 + }),
     33 + padding: 0,
     34 + },
     35 + createButton: {
     36 + position: 'fixed',
     37 + bottom: 30,
     38 + right: 230,
     39 + },
     40 + closeButton: {
     41 + position: 'absolute',
     42 + top: 12,
     43 + left: 5,
     44 + color: 'inherit',
     45 + },
     46 + header: {
     47 + backgroundColor: theme.palette.background.nav,
     48 + padding: '20px 20px 20px 60px',
     49 + },
     50 + container: {
     51 + padding: '10px 20px 20px 20px',
     52 + },
     53 + buttons: {
     54 + marginTop: 20,
     55 + textAlign: 'right',
     56 + },
     57 + button: {
     58 + marginLeft: theme.spacing(2),
     59 + },
     60 +}));
     61 + 
     62 +const vocabularyAdd = graphql`
     63 + mutation VocabularyCreationMutation($input: VocabularyAddInput!) {
     64 + vocabularyAdd(input: $input) {
     65 + ...AttributeLine_node
     66 + }
     67 + }
     68 +`;
     69 + 
     70 +const labelValidation = (t: (v: string) => string) => Yup.object().shape({
     71 + name: Yup.string().required(t('This field is required')),
     72 +});
     73 + 
     74 +const VocabularyCreation: FunctionComponent<VocabularyCreationProps> = ({ category, paginationOptions }) => {
     75 + const classes = useStyles();
     76 + const { t } = useFormatter();
     77 + 
     78 + const [open, setOpen] = useState(false);
     79 + const [addVocab] = useMutation<VocabularyCreationMutation>(vocabularyAdd);
     80 + 
     81 + const handleClose = () => setOpen(false);
     82 + 
     83 + const onSubmit: FormikConfig<{ name: string, label: string }>['onSubmit'] = (values, { resetForm }) => {
     84 + addVocab({
     85 + variables: {
     86 + input: { ...values, category },
     87 + },
     88 + updater: (store) => insertNode(
     89 + store,
     90 + 'Pagination_vocabularies',
     91 + paginationOptions,
     92 + 'vocabularyAdd',
     93 + ),
     94 + onCompleted: () => {
     95 + resetForm();
     96 + handleClose();
     97 + },
     98 + });
     99 + };
     100 + 
     101 + return (
     102 + <div>
     103 + <Fab
     104 + onClick={() => setOpen(true)}
     105 + color="secondary"
     106 + aria-label="Add"
     107 + className={classes.createButton}
     108 + >
     109 + <Add />
     110 + </Fab>
     111 + <Drawer
     112 + open={open}
     113 + anchor="right"
     114 + sx={{ zIndex: 1202 }}
     115 + elevation={1}
     116 + classes={{ paper: classes.drawerPaper }}
     117 + onClose={handleClose}
     118 + >
     119 + <div className={classes.header}>
     120 + <IconButton
     121 + aria-label="Close"
     122 + className={classes.closeButton}
     123 + onClick={handleClose}
     124 + size="large"
     125 + color="primary"
     126 + >
     127 + <Close fontSize="small" color="primary" />
     128 + </IconButton>
     129 + <Typography variant="h6">{t('Create a vocabulary')}</Typography>
     130 + </div>
     131 + <div className={classes.container}>
     132 + <Formik
     133 + initialValues={{
     134 + name: '',
     135 + label: '',
     136 + }}
     137 + validationSchema={labelValidation(t)}
     138 + onSubmit={onSubmit}
     139 + onReset={handleClose}
     140 + >
     141 + {({ submitForm, handleReset, isSubmitting }) => (
     142 + <Form style={{ margin: '20px 0 20px 0' }}>
     143 + <Field
     144 + component={TextField}
     145 + variant="standard"
     146 + name="name"
     147 + label={t('Name')}
     148 + fullWidth={true}
     149 + />
     150 + <Field
     151 + component={TextField}
     152 + variant="standard"
     153 + name="label"
     154 + label={t('Label')}
     155 + fullWidth={true}
     156 + />
     157 + <div className={classes.buttons}>
     158 + <Button
     159 + variant="contained"
     160 + onClick={handleReset}
     161 + disabled={isSubmitting}
     162 + classes={{ root: classes.button }}
     163 + >
     164 + {t('Cancel')}
     165 + </Button>
     166 + <Button
     167 + variant="contained"
     168 + color="secondary"
     169 + onClick={submitForm}
     170 + disabled={isSubmitting}
     171 + classes={{ root: classes.button }}
     172 + >
     173 + {t('Create')}
     174 + </Button>
     175 + </div>
     176 + </Form>
     177 + )}
     178 + </Formik>
     179 + </div>
     180 + </Drawer>
     181 + </div>
     182 + );
     183 +};
     184 + 
     185 +export default VocabularyCreation;
     186 + 
  • ■ ■ ■ ■
    opencti-platform/opencti-front/src/private/components/settings/labels/LabelCreation.js
    1 1  import React, { Component } from 'react';
    2 2  import * as PropTypes from 'prop-types';
    3  -import { Formik, Form, Field } from 'formik';
     3 +import { Field, Form, Formik } from 'formik';
    4 4  import withStyles from '@mui/styles/withStyles';
    5 5  import Drawer from '@mui/material/Drawer';
    6 6  import Typography from '@mui/material/Typography';
    skipped 321 lines
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/relay/environment.js
    skipped 81 lines
    82 82   query={query}
    83 83   variables={variables}
    84 84   render={(data) => {
     85 + console.log('COUCOU', data);
    85 86   const { error } = data;
    86 87   const types = error ? map((e) => e.name, error) : [];
    87 88   const unmanagedErrors = difference(types, managedErrorTypes || []);
    skipped 86 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-front/src/schema/relay.schema.graphql
    skipped 7143 lines
    7144 7144   groupingContainsStixObjectOrStixRelationship(id: String!, stixObjectOrStixRelationshipId: String!): Boolean
    7145 7145   narrative(id: String!): Narrative
    7146 7146   narratives(first: Int, after: ID, orderBy: NarrativesOrdering, orderMode: OrderingMode, filters: [NarrativesFiltering!], filterMode: FilterMode, search: String): NarrativeConnection
     7147 + vocabulary(id: String!): Vocabulary
     7148 + vocabularyCategories: [String!]!
     7149 + vocabularies(category: VocabularyCategory, first: Int, after: ID, orderBy: VocabularyOrdering, orderMode: OrderingMode, filters: [VocabularyFiltering!], filterMode: FilterMode, search: String): VocabularyConnection
    7147 7150  }
    7148 7151   
    7149 7152  type Subscription {
    skipped 617 lines
    7767 7770   narrativeContextClean(id: ID!): Narrative
    7768 7771   narrativeRelationAdd(id: ID!, input: StixMetaRelationshipAddInput!): StixMetaRelationship
    7769 7772   narrativeRelationDelete(id: ID!, toId: StixRef!, relationship_type: String!): Narrative
     7773 + vocabularyAdd(input: VocabularyAddInput!): Vocabulary
    7770 7774  }
    7771 7775   
    7772 7776  type Channel implements BasicObject & StixObject & StixCoreObject & StixDomainObject {
    skipped 533 lines
    8306 8310   clientMutationId: String
    8307 8311   update: Boolean
    8308 8312  }
     8313 + 
     8314 +enum VocabularyCategory {
     8315 + report_type
     8316 + channel_types
     8317 +}
     8318 + 
     8319 +type Vocabulary implements BasicObject {
     8320 + id: ID!
     8321 + standard_id: String!
     8322 + entity_type: String!
     8323 + parent_types: [String]!
     8324 + category: VocabularyCategory!
     8325 + name: String!
     8326 + label: String
     8327 +}
     8328 + 
     8329 +enum VocabularyOrdering {
     8330 + name
     8331 + category
     8332 + label
     8333 +}
     8334 + 
     8335 +enum VocabularyFilter {
     8336 + name
     8337 + category
     8338 + label
     8339 +}
     8340 + 
     8341 +input VocabularyFiltering {
     8342 + key: [VocabularyFilter!]!
     8343 + values: [String]
     8344 + operator: String
     8345 + filterMode: FilterMode
     8346 +}
     8347 + 
     8348 +type VocabularyConnection {
     8349 + pageInfo: PageInfo!
     8350 + edges: [VocabularyEdge]
     8351 +}
     8352 + 
     8353 +type VocabularyEdge {
     8354 + cursor: String!
     8355 + node: Vocabulary!
     8356 +}
     8357 + 
     8358 +input VocabularyAddInput {
     8359 + name: name_String_NotNull_minLength_2!
     8360 + label: String
     8361 + category: String!
     8362 +}
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/graphql-codegen.yml
    skipped 17 lines
    18 18   Event: ../modules/event/event-types#BasicStoreEntityEvent
    19 19   Narrative: ../modules/narrative/narrative-types#BasicStoreEntityNarrative
    20 20   Grouping: ../modules/grouping/grouping-types#BasicStoreEntityGrouping
     21 + Vocabulary: ../modules/vocabulary/vocabulary-types#BasicStoreEntityVocabulary
    21 22   ./graphql.schema.json:
    22 23   plugins:
    23 24   - "introspection"
    skipped 1 lines
  • ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/database/engine.js
    skipped 1773 lines
    1774 1774   values: {
    1775 1775   terms: {
    1776 1776   field: isDateOrNumber ? field : `${field}.keyword`,
    1777  - size: first,
     1777 + size: first ?? MAX_JS_PARAMS,
    1778 1778   order: { _key: orderMode },
    1779 1779   },
    1780 1780   },
    skipped 515 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/generated/graphql.ts
    skipped 3 lines
    4 4  import type { BasicStoreEntityEvent } from '../modules/event/event-types';
    5 5  import type { BasicStoreEntityNarrative } from '../modules/narrative/narrative-types';
    6 6  import type { BasicStoreEntityGrouping } from '../modules/grouping/grouping-types';
     7 +import type { BasicStoreEntityVocabulary } from '../modules/vocabulary/vocabulary-types';
    7 8  export type Maybe<T> = T | null;
    8 9  export type InputMaybe<T> = Maybe<T>;
    9 10  export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
    skipped 7732 lines
    7742 7743   userSessionsKill?: Maybe<Array<Maybe<Scalars['ID']>>>;
    7743 7744   userSubscriptionAdd?: Maybe<UserSubscription>;
    7744 7745   userSubscriptionEdit?: Maybe<UserSubscriptionEditMutations>;
     7746 + vocabularyAdd?: Maybe<Vocabulary>;
    7745 7747   vulnerabilityAdd?: Maybe<Vulnerability>;
    7746 7748   vulnerabilityEdit?: Maybe<VulnerabilityEditMutations>;
    7747 7749   workAdd: Work;
    skipped 951 lines
    8699 8701   
    8700 8702  export type MutationUserSubscriptionEditArgs = {
    8701 8703   id: Scalars['ID'];
     8704 +};
     8705 + 
     8706 + 
     8707 +export type MutationVocabularyAddArgs = {
     8708 + input: VocabularyAddInput;
    8702 8709  };
    8703 8710   
    8704 8711   
    skipped 2515 lines
    11220 11227   userSubscription?: Maybe<UserSubscription>;
    11221 11228   userSubscriptions?: Maybe<UserSubscriptionConnection>;
    11222 11229   users?: Maybe<UserConnection>;
     11230 + vocabularies?: Maybe<VocabularyConnection>;
     11231 + vocabulary?: Maybe<Vocabulary>;
     11232 + vocabularyCategories: Array<Scalars['String']>;
    11223 11233   vulnerabilities?: Maybe<VulnerabilityConnection>;
    11224 11234   vulnerability?: Maybe<Vulnerability>;
    11225 11235   work?: Maybe<Work>;
    skipped 1509 lines
    12735 12745  };
    12736 12746   
    12737 12747   
     12748 +export type QueryVocabulariesArgs = {
     12749 + after?: InputMaybe<Scalars['ID']>;
     12750 + category?: InputMaybe<VocabularyCategory>;
     12751 + filterMode?: InputMaybe<FilterMode>;
     12752 + filters?: InputMaybe<Array<VocabularyFiltering>>;
     12753 + first?: InputMaybe<Scalars['Int']>;
     12754 + orderBy?: InputMaybe<VocabularyOrdering>;
     12755 + orderMode?: InputMaybe<OrderingMode>;
     12756 + search?: InputMaybe<Scalars['String']>;
     12757 +};
     12758 + 
     12759 + 
     12760 +export type QueryVocabularyArgs = {
     12761 + id: Scalars['String'];
     12762 +};
     12763 + 
     12764 + 
    12738 12765  export type QueryVulnerabilitiesArgs = {
    12739 12766   after?: InputMaybe<Scalars['ID']>;
    12740 12767   filterMode?: InputMaybe<FilterMode>;
    skipped 4880 lines
    17621 17648   UserEmail = 'user_email'
    17622 17649  }
    17623 17650   
     17651 +export type Vocabulary = BasicObject & {
     17652 + __typename?: 'Vocabulary';
     17653 + category: VocabularyCategory;
     17654 + entity_type: Scalars['String'];
     17655 + id: Scalars['ID'];
     17656 + label?: Maybe<Scalars['String']>;
     17657 + name: Scalars['String'];
     17658 + parent_types: Array<Maybe<Scalars['String']>>;
     17659 + standard_id: Scalars['String'];
     17660 +};
     17661 + 
     17662 +export type VocabularyAddInput = {
     17663 + category: Scalars['String'];
     17664 + label?: InputMaybe<Scalars['String']>;
     17665 + name: Scalars['String'];
     17666 +};
     17667 + 
     17668 +export enum VocabularyCategory {
     17669 + ChannelTypes = 'channel_types',
     17670 + ReportType = 'report_type'
     17671 +}
     17672 + 
     17673 +export type VocabularyConnection = {
     17674 + __typename?: 'VocabularyConnection';
     17675 + edges?: Maybe<Array<Maybe<VocabularyEdge>>>;
     17676 + pageInfo: PageInfo;
     17677 +};
     17678 + 
     17679 +export type VocabularyEdge = {
     17680 + __typename?: 'VocabularyEdge';
     17681 + cursor: Scalars['String'];
     17682 + node: Vocabulary;
     17683 +};
     17684 + 
     17685 +export enum VocabularyFilter {
     17686 + Category = 'category',
     17687 + Label = 'label',
     17688 + Name = 'name'
     17689 +}
     17690 + 
     17691 +export type VocabularyFiltering = {
     17692 + filterMode?: InputMaybe<FilterMode>;
     17693 + key: Array<VocabularyFilter>;
     17694 + operator?: InputMaybe<Scalars['String']>;
     17695 + values?: InputMaybe<Array<InputMaybe<Scalars['String']>>>;
     17696 +};
     17697 + 
     17698 +export enum VocabularyOrdering {
     17699 + Category = 'category',
     17700 + Label = 'label',
     17701 + Name = 'name'
     17702 +}
     17703 + 
    17624 17704  export enum VulnerabilitiesFilter {
    17625 17705   Created = 'created',
    17626 17706   CreatedBy = 'createdBy',
    skipped 1084 lines
    18711 18791   AutonomousSystemAddInput: AutonomousSystemAddInput;
    18712 18792   BankAccount: ResolverTypeWrapper<Omit<BankAccount, 'connectors' | 'createdBy' | 'creator' | 'exportFiles' | 'externalReferences' | 'groupings' | 'importFiles' | 'indicators' | 'jobs' | 'notes' | 'observedData' | 'opinions' | 'pendingFiles' | 'reports' | 'stixCoreRelationships' | 'stixCyberObservableRelationships'> & { connectors?: Maybe<Array<Maybe<ResolversTypes['Connector']>>>, createdBy?: Maybe<ResolversTypes['Identity']>, creator?: Maybe<ResolversTypes['User']>, exportFiles?: Maybe<ResolversTypes['FileConnection']>, externalReferences?: Maybe<ResolversTypes['ExternalReferenceConnection']>, groupings?: Maybe<ResolversTypes['GroupingConnection']>, importFiles?: Maybe<ResolversTypes['FileConnection']>, indicators?: Maybe<ResolversTypes['IndicatorConnection']>, jobs?: Maybe<Array<Maybe<ResolversTypes['Work']>>>, notes?: Maybe<ResolversTypes['NoteConnection']>, observedData?: Maybe<ResolversTypes['ObservedDataConnection']>, opinions?: Maybe<ResolversTypes['OpinionConnection']>, pendingFiles?: Maybe<ResolversTypes['FileConnection']>, reports?: Maybe<ResolversTypes['ReportConnection']>, stixCoreRelationships?: Maybe<ResolversTypes['StixCoreRelationshipConnection']>, stixCyberObservableRelationships?: Maybe<ResolversTypes['StixCyberObservableRelationshipConnection']> }>;
    18713 18793   BankAccountAddInput: BankAccountAddInput;
    18714  - BasicObject: ResolversTypes['Artifact'] | ResolversTypes['AttackPattern'] | ResolversTypes['AutonomousSystem'] | ResolversTypes['BankAccount'] | ResolversTypes['Campaign'] | ResolversTypes['Capability'] | ResolversTypes['Channel'] | ResolversTypes['City'] | ResolversTypes['Connector'] | ResolversTypes['Country'] | ResolversTypes['CourseOfAction'] | ResolversTypes['CryptocurrencyWallet'] | ResolversTypes['CryptographicKey'] | ResolversTypes['Directory'] | ResolversTypes['DomainName'] | ResolversTypes['EmailAddr'] | ResolversTypes['EmailMessage'] | ResolversTypes['EmailMimePartType'] | ResolversTypes['Event'] | ResolversTypes['ExternalReference'] | ResolversTypes['Group'] | ResolversTypes['Grouping'] | ResolversTypes['Hostname'] | ResolversTypes['IPv4Addr'] | ResolversTypes['IPv6Addr'] | ResolversTypes['Incident'] | ResolversTypes['Indicator'] | ResolversTypes['Individual'] | ResolversTypes['Infrastructure'] | ResolversTypes['IntrusionSet'] | ResolversTypes['KillChainPhase'] | ResolversTypes['Label'] | ResolversTypes['Language'] | ResolversTypes['MacAddr'] | ResolversTypes['Malware'] | ResolversTypes['MarkingDefinition'] | ResolversTypes['MeUser'] | ResolversTypes['MediaContent'] | ResolversTypes['Mutex'] | ResolversTypes['Narrative'] | ResolversTypes['NetworkTraffic'] | ResolversTypes['Note'] | ResolversTypes['ObservedData'] | ResolversTypes['Opinion'] | ResolversTypes['Organization'] | ResolversTypes['PaymentCard'] | ResolversTypes['PhoneNumber'] | ResolversTypes['Position'] | ResolversTypes['Process'] | ResolversTypes['Region'] | ResolversTypes['Report'] | ResolversTypes['Role'] | ResolversTypes['Sector'] | ResolversTypes['Settings'] | ResolversTypes['Software'] | ResolversTypes['StixFile'] | ResolversTypes['System'] | ResolversTypes['Text'] | ResolversTypes['ThreatActor'] | ResolversTypes['Tool'] | ResolversTypes['Url'] | ResolversTypes['User'] | ResolversTypes['UserAccount'] | ResolversTypes['UserAgent'] | ResolversTypes['Vulnerability'] | ResolversTypes['WindowsRegistryKey'] | ResolversTypes['WindowsRegistryValueType'] | ResolversTypes['X509Certificate'];
     18794 + BasicObject: ResolversTypes['Artifact'] | ResolversTypes['AttackPattern'] | ResolversTypes['AutonomousSystem'] | ResolversTypes['BankAccount'] | ResolversTypes['Campaign'] | ResolversTypes['Capability'] | ResolversTypes['Channel'] | ResolversTypes['City'] | ResolversTypes['Connector'] | ResolversTypes['Country'] | ResolversTypes['CourseOfAction'] | ResolversTypes['CryptocurrencyWallet'] | ResolversTypes['CryptographicKey'] | ResolversTypes['Directory'] | ResolversTypes['DomainName'] | ResolversTypes['EmailAddr'] | ResolversTypes['EmailMessage'] | ResolversTypes['EmailMimePartType'] | ResolversTypes['Event'] | ResolversTypes['ExternalReference'] | ResolversTypes['Group'] | ResolversTypes['Grouping'] | ResolversTypes['Hostname'] | ResolversTypes['IPv4Addr'] | ResolversTypes['IPv6Addr'] | ResolversTypes['Incident'] | ResolversTypes['Indicator'] | ResolversTypes['Individual'] | ResolversTypes['Infrastructure'] | ResolversTypes['IntrusionSet'] | ResolversTypes['KillChainPhase'] | ResolversTypes['Label'] | ResolversTypes['Language'] | ResolversTypes['MacAddr'] | ResolversTypes['Malware'] | ResolversTypes['MarkingDefinition'] | ResolversTypes['MeUser'] | ResolversTypes['MediaContent'] | ResolversTypes['Mutex'] | ResolversTypes['Narrative'] | ResolversTypes['NetworkTraffic'] | ResolversTypes['Note'] | ResolversTypes['ObservedData'] | ResolversTypes['Opinion'] | ResolversTypes['Organization'] | ResolversTypes['PaymentCard'] | ResolversTypes['PhoneNumber'] | ResolversTypes['Position'] | ResolversTypes['Process'] | ResolversTypes['Region'] | ResolversTypes['Report'] | ResolversTypes['Role'] | ResolversTypes['Sector'] | ResolversTypes['Settings'] | ResolversTypes['Software'] | ResolversTypes['StixFile'] | ResolversTypes['System'] | ResolversTypes['Text'] | ResolversTypes['ThreatActor'] | ResolversTypes['Tool'] | ResolversTypes['Url'] | ResolversTypes['User'] | ResolversTypes['UserAccount'] | ResolversTypes['UserAgent'] | ResolversTypes['Vocabulary'] | ResolversTypes['Vulnerability'] | ResolversTypes['WindowsRegistryKey'] | ResolversTypes['WindowsRegistryValueType'] | ResolversTypes['X509Certificate'];
    18715 18795   BasicRelationship: ResolversTypes['InternalRelationship'] | ResolversTypes['StixCoreRelationship'] | ResolversTypes['StixCyberObservableRelationship'] | ResolversTypes['StixMetaRelationship'] | ResolversTypes['StixSightingRelationship'];
    18716 18796   Boolean: ResolverTypeWrapper<Scalars['Boolean']>;
    18717 18797   Campaign: ResolverTypeWrapper<Omit<Campaign, 'connectors' | 'createdBy' | 'creator' | 'exportFiles' | 'externalReferences' | 'groupings' | 'importFiles' | 'jobs' | 'notes' | 'observedData' | 'opinions' | 'pendingFiles' | 'reports' | 'stixCoreRelationships'> & { connectors?: Maybe<Array<Maybe<ResolversTypes['Connector']>>>, createdBy?: Maybe<ResolversTypes['Identity']>, creator?: Maybe<ResolversTypes['User']>, exportFiles?: Maybe<ResolversTypes['FileConnection']>, externalReferences?: Maybe<ResolversTypes['ExternalReferenceConnection']>, groupings?: Maybe<ResolversTypes['GroupingConnection']>, importFiles?: Maybe<ResolversTypes['FileConnection']>, jobs?: Maybe<Array<Maybe<ResolversTypes['Work']>>>, notes?: Maybe<ResolversTypes['NoteConnection']>, observedData?: Maybe<ResolversTypes['ObservedDataConnection']>, opinions?: Maybe<ResolversTypes['OpinionConnection']>, pendingFiles?: Maybe<ResolversTypes['FileConnection']>, reports?: Maybe<ResolversTypes['ReportConnection']>, stixCoreRelationships?: Maybe<ResolversTypes['StixCoreRelationshipConnection']> }>;
    skipped 558 lines
    19276 19356   UsersFilter: UsersFilter;
    19277 19357   UsersFiltering: UsersFiltering;
    19278 19358   UsersOrdering: UsersOrdering;
     19359 + Vocabulary: ResolverTypeWrapper<BasicStoreEntityVocabulary>;
     19360 + VocabularyAddInput: VocabularyAddInput;
     19361 + VocabularyCategory: VocabularyCategory;
     19362 + VocabularyConnection: ResolverTypeWrapper<Omit<VocabularyConnection, 'edges'> & { edges?: Maybe<Array<Maybe<ResolversTypes['VocabularyEdge']>>> }>;
     19363 + VocabularyEdge: ResolverTypeWrapper<Omit<VocabularyEdge, 'node'> & { node: ResolversTypes['Vocabulary'] }>;
     19364 + VocabularyFilter: VocabularyFilter;
     19365 + VocabularyFiltering: VocabularyFiltering;
     19366 + VocabularyOrdering: VocabularyOrdering;
    19279 19367   VulnerabilitiesFilter: VulnerabilitiesFilter;
    19280 19368   VulnerabilitiesFiltering: VulnerabilitiesFiltering;
    19281 19369   VulnerabilitiesOrdering: VulnerabilitiesOrdering;
    skipped 55 lines
    19337 19425   AutonomousSystemAddInput: AutonomousSystemAddInput;
    19338 19426   BankAccount: Omit<BankAccount, 'connectors' | 'createdBy' | 'creator' | 'exportFiles' | 'externalReferences' | 'groupings' | 'importFiles' | 'indicators' | 'jobs' | 'notes' | 'observedData' | 'opinions' | 'pendingFiles' | 'reports' | 'stixCoreRelationships' | 'stixCyberObservableRelationships'> & { connectors?: Maybe<Array<Maybe<ResolversParentTypes['Connector']>>>, createdBy?: Maybe<ResolversParentTypes['Identity']>, creator?: Maybe<ResolversParentTypes['User']>, exportFiles?: Maybe<ResolversParentTypes['FileConnection']>, externalReferences?: Maybe<ResolversParentTypes['ExternalReferenceConnection']>, groupings?: Maybe<ResolversParentTypes['GroupingConnection']>, importFiles?: Maybe<ResolversParentTypes['FileConnection']>, indicators?: Maybe<ResolversParentTypes['IndicatorConnection']>, jobs?: Maybe<Array<Maybe<ResolversParentTypes['Work']>>>, notes?: Maybe<ResolversParentTypes['NoteConnection']>, observedData?: Maybe<ResolversParentTypes['ObservedDataConnection']>, opinions?: Maybe<ResolversParentTypes['OpinionConnection']>, pendingFiles?: Maybe<ResolversParentTypes['FileConnection']>, reports?: Maybe<ResolversParentTypes['ReportConnection']>, stixCoreRelationships?: Maybe<ResolversParentTypes['StixCoreRelationshipConnection']>, stixCyberObservableRelationships?: Maybe<ResolversParentTypes['StixCyberObservableRelationshipConnection']> };
    19339 19427   BankAccountAddInput: BankAccountAddInput;
    19340  - BasicObject: ResolversParentTypes['Artifact'] | ResolversParentTypes['AttackPattern'] | ResolversParentTypes['AutonomousSystem'] | ResolversParentTypes['BankAccount'] | ResolversParentTypes['Campaign'] | ResolversParentTypes['Capability'] | ResolversParentTypes['Channel'] | ResolversParentTypes['City'] | ResolversParentTypes['Connector'] | ResolversParentTypes['Country'] | ResolversParentTypes['CourseOfAction'] | ResolversParentTypes['CryptocurrencyWallet'] | ResolversParentTypes['CryptographicKey'] | ResolversParentTypes['Directory'] | ResolversParentTypes['DomainName'] | ResolversParentTypes['EmailAddr'] | ResolversParentTypes['EmailMessage'] | ResolversParentTypes['EmailMimePartType'] | ResolversParentTypes['Event'] | ResolversParentTypes['ExternalReference'] | ResolversParentTypes['Group'] | ResolversParentTypes['Grouping'] | ResolversParentTypes['Hostname'] | ResolversParentTypes['IPv4Addr'] | ResolversParentTypes['IPv6Addr'] | ResolversParentTypes['Incident'] | ResolversParentTypes['Indicator'] | ResolversParentTypes['Individual'] | ResolversParentTypes['Infrastructure'] | ResolversParentTypes['IntrusionSet'] | ResolversParentTypes['KillChainPhase'] | ResolversParentTypes['Label'] | ResolversParentTypes['Language'] | ResolversParentTypes['MacAddr'] | ResolversParentTypes['Malware'] | ResolversParentTypes['MarkingDefinition'] | ResolversParentTypes['MeUser'] | ResolversParentTypes['MediaContent'] | ResolversParentTypes['Mutex'] | ResolversParentTypes['Narrative'] | ResolversParentTypes['NetworkTraffic'] | ResolversParentTypes['Note'] | ResolversParentTypes['ObservedData'] | ResolversParentTypes['Opinion'] | ResolversParentTypes['Organization'] | ResolversParentTypes['PaymentCard'] | ResolversParentTypes['PhoneNumber'] | ResolversParentTypes['Position'] | ResolversParentTypes['Process'] | ResolversParentTypes['Region'] | ResolversParentTypes['Report'] | ResolversParentTypes['Role'] | ResolversParentTypes['Sector'] | ResolversParentTypes['Settings'] | ResolversParentTypes['Software'] | ResolversParentTypes['StixFile'] | ResolversParentTypes['System'] | ResolversParentTypes['Text'] | ResolversParentTypes['ThreatActor'] | ResolversParentTypes['Tool'] | ResolversParentTypes['Url'] | ResolversParentTypes['User'] | ResolversParentTypes['UserAccount'] | ResolversParentTypes['UserAgent'] | ResolversParentTypes['Vulnerability'] | ResolversParentTypes['WindowsRegistryKey'] | ResolversParentTypes['WindowsRegistryValueType'] | ResolversParentTypes['X509Certificate'];
     19428 + BasicObject: ResolversParentTypes['Artifact'] | ResolversParentTypes['AttackPattern'] | ResolversParentTypes['AutonomousSystem'] | ResolversParentTypes['BankAccount'] | ResolversParentTypes['Campaign'] | ResolversParentTypes['Capability'] | ResolversParentTypes['Channel'] | ResolversParentTypes['City'] | ResolversParentTypes['Connector'] | ResolversParentTypes['Country'] | ResolversParentTypes['CourseOfAction'] | ResolversParentTypes['CryptocurrencyWallet'] | ResolversParentTypes['CryptographicKey'] | ResolversParentTypes['Directory'] | ResolversParentTypes['DomainName'] | ResolversParentTypes['EmailAddr'] | ResolversParentTypes['EmailMessage'] | ResolversParentTypes['EmailMimePartType'] | ResolversParentTypes['Event'] | ResolversParentTypes['ExternalReference'] | ResolversParentTypes['Group'] | ResolversParentTypes['Grouping'] | ResolversParentTypes['Hostname'] | ResolversParentTypes['IPv4Addr'] | ResolversParentTypes['IPv6Addr'] | ResolversParentTypes['Incident'] | ResolversParentTypes['Indicator'] | ResolversParentTypes['Individual'] | ResolversParentTypes['Infrastructure'] | ResolversParentTypes['IntrusionSet'] | ResolversParentTypes['KillChainPhase'] | ResolversParentTypes['Label'] | ResolversParentTypes['Language'] | ResolversParentTypes['MacAddr'] | ResolversParentTypes['Malware'] | ResolversParentTypes['MarkingDefinition'] | ResolversParentTypes['MeUser'] | ResolversParentTypes['MediaContent'] | ResolversParentTypes['Mutex'] | ResolversParentTypes['Narrative'] | ResolversParentTypes['NetworkTraffic'] | ResolversParentTypes['Note'] | ResolversParentTypes['ObservedData'] | ResolversParentTypes['Opinion'] | ResolversParentTypes['Organization'] | ResolversParentTypes['PaymentCard'] | ResolversParentTypes['PhoneNumber'] | ResolversParentTypes['Position'] | ResolversParentTypes['Process'] | ResolversParentTypes['Region'] | ResolversParentTypes['Report'] | ResolversParentTypes['Role'] | ResolversParentTypes['Sector'] | ResolversParentTypes['Settings'] | ResolversParentTypes['Software'] | ResolversParentTypes['StixFile'] | ResolversParentTypes['System'] | ResolversParentTypes['Text'] | ResolversParentTypes['ThreatActor'] | ResolversParentTypes['Tool'] | ResolversParentTypes['Url'] | ResolversParentTypes['User'] | ResolversParentTypes['UserAccount'] | ResolversParentTypes['UserAgent'] | ResolversParentTypes['Vocabulary'] | ResolversParentTypes['Vulnerability'] | ResolversParentTypes['WindowsRegistryKey'] | ResolversParentTypes['WindowsRegistryValueType'] | ResolversParentTypes['X509Certificate'];
    19341 19429   BasicRelationship: ResolversParentTypes['InternalRelationship'] | ResolversParentTypes['StixCoreRelationship'] | ResolversParentTypes['StixCyberObservableRelationship'] | ResolversParentTypes['StixMetaRelationship'] | ResolversParentTypes['StixSightingRelationship'];
    19342 19430   Boolean: Scalars['Boolean'];
    19343 19431   Campaign: Omit<Campaign, 'connectors' | 'createdBy' | 'creator' | 'exportFiles' | 'externalReferences' | 'groupings' | 'importFiles' | 'jobs' | 'notes' | 'observedData' | 'opinions' | 'pendingFiles' | 'reports' | 'stixCoreRelationships'> & { connectors?: Maybe<Array<Maybe<ResolversParentTypes['Connector']>>>, createdBy?: Maybe<ResolversParentTypes['Identity']>, creator?: Maybe<ResolversParentTypes['User']>, exportFiles?: Maybe<ResolversParentTypes['FileConnection']>, externalReferences?: Maybe<ResolversParentTypes['ExternalReferenceConnection']>, groupings?: Maybe<ResolversParentTypes['GroupingConnection']>, importFiles?: Maybe<ResolversParentTypes['FileConnection']>, jobs?: Maybe<Array<Maybe<ResolversParentTypes['Work']>>>, notes?: Maybe<ResolversParentTypes['NoteConnection']>, observedData?: Maybe<ResolversParentTypes['ObservedDataConnection']>, opinions?: Maybe<ResolversParentTypes['OpinionConnection']>, pendingFiles?: Maybe<ResolversParentTypes['FileConnection']>, reports?: Maybe<ResolversParentTypes['ReportConnection']>, stixCoreRelationships?: Maybe<ResolversParentTypes['StixCoreRelationshipConnection']> };
    skipped 444 lines
    19788 19876   UserSubscriptionEdge: Omit<UserSubscriptionEdge, 'node'> & { node: ResolversParentTypes['UserSubscription'] };
    19789 19877   UserSubscriptionEditMutations: Omit<UserSubscriptionEditMutations, 'fieldPatch'> & { fieldPatch?: Maybe<ResolversParentTypes['UserSubscription']> };
    19790 19878   UsersFiltering: UsersFiltering;
     19879 + Vocabulary: BasicStoreEntityVocabulary;
     19880 + VocabularyAddInput: VocabularyAddInput;
     19881 + VocabularyConnection: Omit<VocabularyConnection, 'edges'> & { edges?: Maybe<Array<Maybe<ResolversParentTypes['VocabularyEdge']>>> };
     19882 + VocabularyEdge: Omit<VocabularyEdge, 'node'> & { node: ResolversParentTypes['Vocabulary'] };
     19883 + VocabularyFiltering: VocabularyFiltering;
    19791 19884   VulnerabilitiesFiltering: VulnerabilitiesFiltering;
    19792 19885   Vulnerability: Omit<Vulnerability, 'connectors' | 'createdBy' | 'creator' | 'exportFiles' | 'externalReferences' | 'groupings' | 'importFiles' | 'jobs' | 'notes' | 'observedData' | 'opinions' | 'pendingFiles' | 'reports' | 'softwares' | 'stixCoreRelationships'> & { connectors?: Maybe<Array<Maybe<ResolversParentTypes['Connector']>>>, createdBy?: Maybe<ResolversParentTypes['Identity']>, creator?: Maybe<ResolversParentTypes['User']>, exportFiles?: Maybe<ResolversParentTypes['FileConnection']>, externalReferences?: Maybe<ResolversParentTypes['ExternalReferenceConnection']>, groupings?: Maybe<ResolversParentTypes['GroupingConnection']>, importFiles?: Maybe<ResolversParentTypes['FileConnection']>, jobs?: Maybe<Array<Maybe<ResolversParentTypes['Work']>>>, notes?: Maybe<ResolversParentTypes['NoteConnection']>, observedData?: Maybe<ResolversParentTypes['ObservedDataConnection']>, opinions?: Maybe<ResolversParentTypes['OpinionConnection']>, pendingFiles?: Maybe<ResolversParentTypes['FileConnection']>, reports?: Maybe<ResolversParentTypes['ReportConnection']>, softwares?: Maybe<ResolversParentTypes['StixCyberObservableConnection']>, stixCoreRelationships?: Maybe<ResolversParentTypes['StixCoreRelationshipConnection']> };
    19793 19886   VulnerabilityAddInput: VulnerabilityAddInput;
    skipped 313 lines
    20107 20200  }>;
    20108 20201   
    20109 20202  export type BasicObjectResolvers<ContextType = any, ParentType extends ResolversParentTypes['BasicObject'] = ResolversParentTypes['BasicObject']> = ResolversObject<{
    20110  - __resolveType: TypeResolveFn<'Artifact' | 'AttackPattern' | 'AutonomousSystem' | 'BankAccount' | 'Campaign' | 'Capability' | 'Channel' | 'City' | 'Connector' | 'Country' | 'CourseOfAction' | 'CryptocurrencyWallet' | 'CryptographicKey' | 'Directory' | 'DomainName' | 'EmailAddr' | 'EmailMessage' | 'EmailMimePartType' | 'Event' | 'ExternalReference' | 'Group' | 'Grouping' | 'Hostname' | 'IPv4Addr' | 'IPv6Addr' | 'Incident' | 'Indicator' | 'Individual' | 'Infrastructure' | 'IntrusionSet' | 'KillChainPhase' | 'Label' | 'Language' | 'MacAddr' | 'Malware' | 'MarkingDefinition' | 'MeUser' | 'MediaContent' | 'Mutex' | 'Narrative' | 'NetworkTraffic' | 'Note' | 'ObservedData' | 'Opinion' | 'Organization' | 'PaymentCard' | 'PhoneNumber' | 'Position' | 'Process' | 'Region' | 'Report' | 'Role' | 'Sector' | 'Settings' | 'Software' | 'StixFile' | 'System' | 'Text' | 'ThreatActor' | 'Tool' | 'Url' | 'User' | 'UserAccount' | 'UserAgent' | 'Vulnerability' | 'WindowsRegistryKey' | 'WindowsRegistryValueType' | 'X509Certificate', ParentType, ContextType>;
     20203 + __resolveType: TypeResolveFn<'Artifact' | 'AttackPattern' | 'AutonomousSystem' | 'BankAccount' | 'Campaign' | 'Capability' | 'Channel' | 'City' | 'Connector' | 'Country' | 'CourseOfAction' | 'CryptocurrencyWallet' | 'CryptographicKey' | 'Directory' | 'DomainName' | 'EmailAddr' | 'EmailMessage' | 'EmailMimePartType' | 'Event' | 'ExternalReference' | 'Group' | 'Grouping' | 'Hostname' | 'IPv4Addr' | 'IPv6Addr' | 'Incident' | 'Indicator' | 'Individual' | 'Infrastructure' | 'IntrusionSet' | 'KillChainPhase' | 'Label' | 'Language' | 'MacAddr' | 'Malware' | 'MarkingDefinition' | 'MeUser' | 'MediaContent' | 'Mutex' | 'Narrative' | 'NetworkTraffic' | 'Note' | 'ObservedData' | 'Opinion' | 'Organization' | 'PaymentCard' | 'PhoneNumber' | 'Position' | 'Process' | 'Region' | 'Report' | 'Role' | 'Sector' | 'Settings' | 'Software' | 'StixFile' | 'System' | 'Text' | 'ThreatActor' | 'Tool' | 'Url' | 'User' | 'UserAccount' | 'UserAgent' | 'Vocabulary' | 'Vulnerability' | 'WindowsRegistryKey' | 'WindowsRegistryValueType' | 'X509Certificate', ParentType, ContextType>;
    20111 20204   entity_type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
    20112 20205   id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
    20113 20206   parent_types?: Resolver<Array<Maybe<ResolversTypes['String']>>, ParentType, ContextType>;
    skipped 2368 lines
    22482 22575   userSessionsKill?: Resolver<Maybe<Array<Maybe<ResolversTypes['ID']>>>, ParentType, ContextType, RequireFields<MutationUserSessionsKillArgs, 'id'>>;
    22483 22576   userSubscriptionAdd?: Resolver<Maybe<ResolversTypes['UserSubscription']>, ParentType, ContextType, Partial<MutationUserSubscriptionAddArgs>>;
    22484 22577   userSubscriptionEdit?: Resolver<Maybe<ResolversTypes['UserSubscriptionEditMutations']>, ParentType, ContextType, RequireFields<MutationUserSubscriptionEditArgs, 'id'>>;
     22578 + vocabularyAdd?: Resolver<Maybe<ResolversTypes['Vocabulary']>, ParentType, ContextType, RequireFields<MutationVocabularyAddArgs, 'input'>>;
    22485 22579   vulnerabilityAdd?: Resolver<Maybe<ResolversTypes['Vulnerability']>, ParentType, ContextType, Partial<MutationVulnerabilityAddArgs>>;
    22486 22580   vulnerabilityEdit?: Resolver<Maybe<ResolversTypes['VulnerabilityEditMutations']>, ParentType, ContextType, RequireFields<MutationVulnerabilityEditArgs, 'id'>>;
    22487 22581   workAdd?: Resolver<ResolversTypes['Work'], ParentType, ContextType, RequireFields<MutationWorkAddArgs, 'connectorId'>>;
    skipped 867 lines
    23355 23449   userSubscription?: Resolver<Maybe<ResolversTypes['UserSubscription']>, ParentType, ContextType, RequireFields<QueryUserSubscriptionArgs, 'id'>>;
    23356 23450   userSubscriptions?: Resolver<Maybe<ResolversTypes['UserSubscriptionConnection']>, ParentType, ContextType, Partial<QueryUserSubscriptionsArgs>>;
    23357 23451   users?: Resolver<Maybe<ResolversTypes['UserConnection']>, ParentType, ContextType, Partial<QueryUsersArgs>>;
     23452 + vocabularies?: Resolver<Maybe<ResolversTypes['VocabularyConnection']>, ParentType, ContextType, Partial<QueryVocabulariesArgs>>;
     23453 + vocabulary?: Resolver<Maybe<ResolversTypes['Vocabulary']>, ParentType, ContextType, RequireFields<QueryVocabularyArgs, 'id'>>;
     23454 + vocabularyCategories?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
    23358 23455   vulnerabilities?: Resolver<Maybe<ResolversTypes['VulnerabilityConnection']>, ParentType, ContextType, Partial<QueryVulnerabilitiesArgs>>;
    23359 23456   vulnerability?: Resolver<Maybe<ResolversTypes['Vulnerability']>, ParentType, ContextType, Partial<QueryVulnerabilityArgs>>;
    23360 23457   work?: Resolver<Maybe<ResolversTypes['Work']>, ParentType, ContextType, RequireFields<QueryWorkArgs, 'id'>>;
    skipped 1744 lines
    25105 25202   __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
    25106 25203  }>;
    25107 25204   
     25205 +export type VocabularyResolvers<ContextType = any, ParentType extends ResolversParentTypes['Vocabulary'] = ResolversParentTypes['Vocabulary']> = ResolversObject<{
     25206 + category?: Resolver<ResolversTypes['VocabularyCategory'], ParentType, ContextType>;
     25207 + entity_type?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
     25208 + id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
     25209 + label?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
     25210 + name?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
     25211 + parent_types?: Resolver<Array<Maybe<ResolversTypes['String']>>, ParentType, ContextType>;
     25212 + standard_id?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
     25213 + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
     25214 +}>;
     25215 + 
     25216 +export type VocabularyConnectionResolvers<ContextType = any, ParentType extends ResolversParentTypes['VocabularyConnection'] = ResolversParentTypes['VocabularyConnection']> = ResolversObject<{
     25217 + edges?: Resolver<Maybe<Array<Maybe<ResolversTypes['VocabularyEdge']>>>, ParentType, ContextType>;
     25218 + pageInfo?: Resolver<ResolversTypes['PageInfo'], ParentType, ContextType>;
     25219 + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
     25220 +}>;
     25221 + 
     25222 +export type VocabularyEdgeResolvers<ContextType = any, ParentType extends ResolversParentTypes['VocabularyEdge'] = ResolversParentTypes['VocabularyEdge']> = ResolversObject<{
     25223 + cursor?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
     25224 + node?: Resolver<ResolversTypes['Vocabulary'], ParentType, ContextType>;
     25225 + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
     25226 +}>;
     25227 + 
    25108 25228  export type VulnerabilityResolvers<ContextType = any, ParentType extends ResolversParentTypes['Vulnerability'] = ResolversParentTypes['Vulnerability']> = ResolversObject<{
    25109 25229   confidence?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
    25110 25230   connectors?: Resolver<Maybe<Array<Maybe<ResolversTypes['Connector']>>>, ParentType, ContextType, Partial<VulnerabilityConnectorsArgs>>;
    skipped 636 lines
    25747 25867   UserSubscriptionConnection?: UserSubscriptionConnectionResolvers<ContextType>;
    25748 25868   UserSubscriptionEdge?: UserSubscriptionEdgeResolvers<ContextType>;
    25749 25869   UserSubscriptionEditMutations?: UserSubscriptionEditMutationsResolvers<ContextType>;
     25870 + Vocabulary?: VocabularyResolvers<ContextType>;
     25871 + VocabularyConnection?: VocabularyConnectionResolvers<ContextType>;
     25872 + VocabularyEdge?: VocabularyEdgeResolvers<ContextType>;
    25750 25873   Vulnerability?: VulnerabilityResolvers<ContextType>;
    25751 25874   VulnerabilityConnection?: VulnerabilityConnectionResolvers<ContextType>;
    25752 25875   VulnerabilityEdge?: VulnerabilityEdgeResolvers<ContextType>;
    skipped 21 lines
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/migrations/1667982050000-vocabulary-mapping.js
     1 +import { elAttributeValues } from '../database/engine';
     2 +import { executionContext, SYSTEM_USER } from '../utils/access';
     3 +import { addVocabulary } from '../modules/vocabulary/vocabulary-domain';
     4 +import { VocabularyCategory } from '../modules/vocabulary/vocabulary-types';
     5 + 
     6 +export const up = async (next) => {
     7 + const context = executionContext('migration');
     8 + await Promise.all(Object.keys(VocabularyCategory).flatMap(async (category) => {
     9 + const { edges } = await elAttributeValues(context, SYSTEM_USER, category);
     10 + const keys = edges.map(({ node: { value } }) => value);
     11 + keys.map(async (name) => {
     12 + await addVocabulary(context, SYSTEM_USER, { name, category });
     13 + });
     14 + }));
     15 + next();
     16 +};
     17 + 
     18 +export const down = async (next) => {
     19 + next();
     20 +};
     21 + 
  • ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/index.ts
    skipped 3 lines
    4 4  import './event/event';
    5 5  import './grouping/grouping';
    6 6  import './narrative/narrative';
     7 +import './vocabulary/vocabulary';
    7 8  // endregion
    8 9   
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary-converter.ts
     1 +import { buildStixObject } from '../../database/stix-converter';
     2 +import type { StixVocabulary, StoreEntityVocabulary } from './vocabulary-types';
     3 + 
     4 +const convertVocabularyToStix = (instance: StoreEntityVocabulary): StixVocabulary => {
     5 + const stixObject = buildStixObject(instance);
     6 + return {
     7 + ...stixObject,
     8 + name: instance.name,
     9 + label: instance.label,
     10 + };
     11 +};
     12 + 
     13 +export default convertVocabularyToStix;
     14 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary-domain.ts
     1 +import type { AuthContext, AuthUser } from '../../types/user';
     2 +import { createEntity, storeLoadById } from '../../database/middleware';
     3 +import type { QueryVocabulariesArgs, VocabularyAddInput } from '../../generated/graphql';
     4 +import { listEntitiesPaginated } from '../../database/middleware-loader';
     5 +import { BasicStoreEntityVocabulary, ENTITY_TYPE_VOCABULARY } from './vocabulary-types';
     6 +import { notify } from '../../database/redis';
     7 +import { BUS_TOPICS } from '../../config/conf';
     8 +import { ABSTRACT_STIX_DOMAIN_OBJECT } from '../../schema/general';
     9 +import { VocabularyFilter } from '../../generated/graphql';
     10 + 
     11 +export const findById = (context: AuthContext, user: AuthUser, narrativeId: string): BasicStoreEntityVocabulary => {
     12 + return storeLoadById(context, user, narrativeId, ENTITY_TYPE_VOCABULARY) as unknown as BasicStoreEntityVocabulary;
     13 +};
     14 + 
     15 +export const findAll = (context: AuthContext, user: AuthUser, opts: QueryVocabulariesArgs) => {
     16 + const { category } = opts;
     17 + const filters = category ? [...(opts.filters ?? []), { key: [VocabularyFilter.Category], values: [category] }] : opts.filters;
     18 + return listEntitiesPaginated<BasicStoreEntityVocabulary>(context, user, [ENTITY_TYPE_VOCABULARY], { ...opts, filters });
     19 +};
     20 + 
     21 +export const getVocabulariesCategories = async (context: AuthContext, user: AuthUser) => {
     22 + const { edges: vocabs } = await findAll(context, user, {});
     23 + return new Set(vocabs.map(({ node: { category } }) => category));
     24 +};
     25 + 
     26 +export const addVocabulary = async (context: AuthContext, user: AuthUser, vocabulary: VocabularyAddInput) => {
     27 + const created = await createEntity(context, user, vocabulary, ENTITY_TYPE_VOCABULARY);
     28 + return notify(BUS_TOPICS[ABSTRACT_STIX_DOMAIN_OBJECT].ADDED_TOPIC, created, user);
     29 +};
     30 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary-resolver.ts
     1 +import type { Resolvers } from '../../generated/graphql';
     2 +import { addVocabulary, findAll, findById, getVocabulariesCategories } from './vocabulary-domain';
     3 + 
     4 +const vocabularyResolvers: Resolvers = {
     5 + Query: {
     6 + vocabulary: (_, { id }, context) => findById(context, context.user, id),
     7 + vocabularies: (_, args, context) => findAll(context, context.user, args),
     8 + vocabularyCategories: (_, __, context) => getVocabulariesCategories(context, context.user),
     9 + },
     10 + Mutation: {
     11 + vocabularyAdd: (_, { input }, context) => {
     12 + return addVocabulary(context, context.user, input);
     13 + },
     14 + },
     15 +};
     16 + 
     17 +export default vocabularyResolvers;
     18 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary-types.ts
     1 +import type { BasicStoreEntity, StoreEntity } from '../../types/store';
     2 +import type { StixObject } from '../../generated/graphql';
     3 + 
     4 +export const ENTITY_TYPE_VOCABULARY = 'Vocabulary';
     5 + 
     6 +// region Database types
     7 +export enum VocabularyCategory {
     8 + report_type = 'report_type',
     9 + channel_types = 'channel_types',
     10 +}
     11 + 
     12 +export interface BasicStoreEntityVocabulary extends BasicStoreEntity {
     13 + name: string;
     14 + label?: string;
     15 + category: VocabularyCategory;
     16 +}
     17 + 
     18 +export interface StoreEntityVocabulary extends StoreEntity {
     19 + name: string;
     20 + label?: string;
     21 + category: VocabularyCategory;
     22 +}
     23 + 
     24 +// region Stix type
     25 +export interface StixVocabulary extends StixObject {
     26 + name: string
     27 + label?: string
     28 + category: VocabularyCategory
     29 +}
     30 + 
     31 +// endregion
     32 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary.graphql
     1 +enum VocabularyCategory {
     2 + report_type
     3 + channel_types
     4 +}
     5 + 
     6 +type Vocabulary implements BasicObject {
     7 + id: ID! # internal_id
     8 + standard_id: String!
     9 + entity_type: String!
     10 + parent_types: [String]!
     11 + # Vocabulary
     12 + category: VocabularyCategory!
     13 + name: String!
     14 + label: String
     15 +}
     16 + 
     17 +# Ordering
     18 +enum VocabularyOrdering {
     19 + name
     20 + category
     21 + label
     22 +}
     23 + 
     24 +# Filtering
     25 +enum VocabularyFilter {
     26 + name
     27 + category
     28 + label
     29 +}
     30 +input VocabularyFiltering {
     31 + key: [VocabularyFilter!]!
     32 + values: [String]
     33 + operator: String
     34 + filterMode: FilterMode
     35 +}
     36 + 
     37 +# Relay connections
     38 +type VocabularyConnection {
     39 + pageInfo: PageInfo!
     40 + edges: [VocabularyEdge]
     41 +}
     42 +type VocabularyEdge {
     43 + cursor: String!
     44 + node: Vocabulary!
     45 +}
     46 + 
     47 +# Queries
     48 +type Query {
     49 + vocabulary(id: String!): Vocabulary @auth(for: [SETTINGS])
     50 + vocabularyCategories: [String!]! @auth(for: [SETTINGS])
     51 + vocabularies(
     52 + category: VocabularyCategory
     53 + first: Int
     54 + after: ID
     55 + orderBy: VocabularyOrdering
     56 + orderMode: OrderingMode
     57 + filters: [VocabularyFiltering!]
     58 + filterMode: FilterMode
     59 + search: String
     60 + ): VocabularyConnection @auth(for: [SETTINGS])
     61 +}
     62 + 
     63 +# Mutations
     64 +input VocabularyAddInput {
     65 + name: String! @constraint(minLength: 2)
     66 + label: String
     67 + category: String!
     68 +}
     69 + 
     70 +type Mutation {
     71 + vocabularyAdd(input: VocabularyAddInput!): Vocabulary @auth(for: [SETTINGS_SETLABELS])
     72 +}
     73 + 
  • ■ ■ ■ ■ ■ ■
    opencti-platform/opencti-graphql/src/modules/vocabulary/vocabulary.ts
     1 +import vocabularyTypeDefs from './vocabulary.graphql';
     2 +import { NAME_FIELD, normalizeName } from '../../schema/identifier';
     3 +import type { ModuleDefinition } from '../../types/module';
     4 +import { registerDefinition } from '../../types/module';
     5 +import { ENTITY_TYPE_VOCABULARY, StoreEntityVocabulary } from './vocabulary-types';
     6 +import vocabularyResolvers from './vocabulary-resolver';
     7 +import convertVocabularyToStix from './vocabulary-converter';
     8 + 
     9 +const VOCABULARY_DEFINITION: ModuleDefinition<StoreEntityVocabulary> = {
     10 + type: {
     11 + id: 'vocabulary',
     12 + name: ENTITY_TYPE_VOCABULARY,
     13 + category: 'StixDomainEntity',
     14 + aliased: true
     15 + },
     16 + graphql: {
     17 + schema: vocabularyTypeDefs,
     18 + resolver: vocabularyResolvers,
     19 + },
     20 + identifier: {
     21 + definition: {
     22 + [ENTITY_TYPE_VOCABULARY]: [{ src: NAME_FIELD }]
     23 + },
     24 + resolvers: {
     25 + name(data: object) {
     26 + return normalizeName(data);
     27 + },
     28 + },
     29 + },
     30 + attributes: [
     31 + { name: 'name', type: 'string', multiple: false, upsert: true },
     32 + { name: 'label', type: 'string', multiple: true, upsert: true },
     33 + { name: 'category', type: 'string', multiple: false, upsert: true },
     34 + ],
     35 + relations: [],
     36 + converter: convertVocabularyToStix,
     37 +};
     38 + 
     39 +registerDefinition(VOCABULARY_DEFINITION);
     40 + 
Please wait...
Page is in error, reload to recover