-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '4.9.0' into feat/6278-replace-discover-docker
- Loading branch information
Showing
9 changed files
with
230 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
312 changes: 168 additions & 144 deletions
312
plugins/main/public/components/common/data-grid/data-grid-service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,163 +1,187 @@ | ||
import { SearchResponse } from "../../../../../../src/core/server"; | ||
import { SearchResponse } from '../../../../../../src/core/server'; | ||
import * as FileSaver from '../../../services/file-saver'; | ||
import { beautifyDate } from "../../agents/vuls/inventory/lib"; | ||
import { SearchParams, search } from "../search-bar/search-bar-service"; | ||
import { IFieldType, IndexPattern } from "../../../../../../src/plugins/data/common"; | ||
import { beautifyDate } from '../../agents/vuls/inventory/lib'; | ||
import { SearchParams, search } from '../search-bar/search-bar-service'; | ||
import { IFieldType } from '../../../../../../src/plugins/data/common'; | ||
export const MAX_ENTRIES_PER_QUERY = 10000; | ||
import { EuiDataGridColumn } from '@elastic/eui'; | ||
|
||
export const parseData = (resultsHits: SearchResponse['hits']['hits']): any[] => { | ||
const data = resultsHits.map((hit) => { | ||
if (!hit) { | ||
return {} | ||
} | ||
const source = hit._source as object; | ||
const data = { | ||
...source, | ||
_id: hit._id, | ||
_index: hit._index, | ||
_type: hit._type, | ||
_score: hit._score, | ||
}; | ||
return data; | ||
}); | ||
export const parseData = ( | ||
resultsHits: SearchResponse['hits']['hits'], | ||
): any[] => { | ||
const data = resultsHits.map(hit => { | ||
if (!hit) { | ||
return {}; | ||
} | ||
const source = hit._source as object; | ||
const data = { | ||
...source, | ||
_id: hit._id, | ||
_index: hit._index, | ||
_type: hit._type, | ||
_score: hit._score, | ||
}; | ||
return data; | ||
} | ||
|
||
}); | ||
return data; | ||
}; | ||
|
||
export const getFieldFormatted = (rowIndex, columnId, indexPattern, rowsParsed) => { | ||
const field = indexPattern.fields.find((field) => field.name === columnId); | ||
let fieldValue = null; | ||
if (columnId.includes('.')) { | ||
// when the column is a nested field. The column could have 2 to n levels | ||
// get dinamically the value of the nested field | ||
const nestedFields = columnId.split('.'); | ||
fieldValue = rowsParsed[rowIndex]; | ||
nestedFields.forEach((field) => { | ||
if (fieldValue) { | ||
fieldValue = fieldValue[field]; | ||
} | ||
}); | ||
export const getFieldFormatted = ( | ||
rowIndex, | ||
columnId, | ||
indexPattern, | ||
rowsParsed, | ||
) => { | ||
const field = indexPattern.fields.find(field => field.name === columnId); | ||
let fieldValue = null; | ||
if (columnId.includes('.')) { | ||
// when the column is a nested field. The column could have 2 to n levels | ||
// get dinamically the value of the nested field | ||
const nestedFields = columnId.split('.'); | ||
fieldValue = rowsParsed[rowIndex]; | ||
nestedFields.forEach(field => { | ||
if (fieldValue) { | ||
fieldValue = fieldValue[field]; | ||
} | ||
}); | ||
} else { | ||
const rowValue = rowsParsed[rowIndex]; | ||
// when not exist the column in the row value then the value is null | ||
if (!rowValue.hasOwnProperty(columnId)) { | ||
fieldValue = null; | ||
} else { | ||
const rowValue = rowsParsed[rowIndex]; | ||
// when not exist the column in the row value then the value is null | ||
if(!rowValue.hasOwnProperty(columnId)){ | ||
fieldValue = null; | ||
}else{ | ||
fieldValue = rowValue[columnId]?.formatted || rowValue[columnId]; | ||
} | ||
} | ||
// when fieldValue is null or undefined then return a empty string | ||
if (fieldValue === null || fieldValue === undefined) { | ||
return ''; | ||
} | ||
// if is date field | ||
if (field?.type === 'date') { | ||
// @ts-ignore | ||
fieldValue = beautifyDate(fieldValue); | ||
fieldValue = rowValue[columnId]?.formatted || rowValue[columnId]; | ||
} | ||
return fieldValue; | ||
} | ||
} | ||
// when fieldValue is null or undefined then return a empty string | ||
if (fieldValue === null || fieldValue === undefined) { | ||
return ''; | ||
} | ||
// if is date field | ||
if (field?.type === 'date') { | ||
// @ts-ignore | ||
fieldValue = beautifyDate(fieldValue); | ||
} | ||
|
||
// if is geo_point field then convert to string to appear in the Discover table | ||
if (field?.type === 'geo_point') { | ||
// @ts-ignore | ||
fieldValue = JSON.stringify(fieldValue); | ||
} | ||
return fieldValue; | ||
}; | ||
|
||
// receive search params | ||
export const exportSearchToCSV = async (params: SearchParams): Promise<void> => { | ||
const DEFAULT_MAX_SIZE_PER_CALL = 1000; | ||
const { indexPattern, filters = [], query, sorting, fields, pagination } = params; | ||
// when the pageSize is greater than the default max size per call (10000) | ||
// then we need to paginate the search | ||
const mustPaginateSearch = pagination?.pageSize && pagination?.pageSize > DEFAULT_MAX_SIZE_PER_CALL; | ||
const pageSize = mustPaginateSearch ? DEFAULT_MAX_SIZE_PER_CALL : pagination?.pageSize; | ||
const totalHits = pagination?.pageSize || DEFAULT_MAX_SIZE_PER_CALL; | ||
let pageIndex = params.pagination?.pageIndex || 0; | ||
let hitsCount = 0; | ||
let allHits = []; | ||
let searchResults; | ||
if (mustPaginateSearch) { | ||
// paginate the search | ||
while (hitsCount < totalHits && hitsCount < MAX_ENTRIES_PER_QUERY) { | ||
const searchParams = { | ||
indexPattern, | ||
filters, | ||
query, | ||
pagination: { | ||
pageIndex, | ||
pageSize, | ||
}, | ||
sorting, | ||
fields, | ||
}; | ||
searchResults = await search(searchParams); | ||
allHits = allHits.concat(searchResults.hits.hits); | ||
hitsCount = allHits.length; | ||
pageIndex++; | ||
} | ||
} else { | ||
searchResults = await search(params); | ||
allHits = searchResults.hits.hits; | ||
export const exportSearchToCSV = async ( | ||
params: SearchParams, | ||
): Promise<void> => { | ||
const DEFAULT_MAX_SIZE_PER_CALL = 1000; | ||
const { | ||
indexPattern, | ||
filters = [], | ||
query, | ||
sorting, | ||
fields, | ||
pagination, | ||
} = params; | ||
// when the pageSize is greater than the default max size per call (10000) | ||
// then we need to paginate the search | ||
const mustPaginateSearch = | ||
pagination?.pageSize && pagination?.pageSize > DEFAULT_MAX_SIZE_PER_CALL; | ||
const pageSize = mustPaginateSearch | ||
? DEFAULT_MAX_SIZE_PER_CALL | ||
: pagination?.pageSize; | ||
const totalHits = pagination?.pageSize || DEFAULT_MAX_SIZE_PER_CALL; | ||
let pageIndex = params.pagination?.pageIndex || 0; | ||
let hitsCount = 0; | ||
let allHits = []; | ||
let searchResults; | ||
if (mustPaginateSearch) { | ||
// paginate the search | ||
while (hitsCount < totalHits && hitsCount < MAX_ENTRIES_PER_QUERY) { | ||
const searchParams = { | ||
indexPattern, | ||
filters, | ||
query, | ||
pagination: { | ||
pageIndex, | ||
pageSize, | ||
}, | ||
sorting, | ||
fields, | ||
}; | ||
searchResults = await search(searchParams); | ||
allHits = allHits.concat(searchResults.hits.hits); | ||
hitsCount = allHits.length; | ||
pageIndex++; | ||
} | ||
|
||
const resultsFields = fields; | ||
const data = allHits.map((hit) => { | ||
// check if the field type is a date | ||
const dateFields = indexPattern.fields.getByType('date'); | ||
const dateFieldsNames = dateFields.map((field) => field.name); | ||
const flattenHit = indexPattern.flattenHit(hit); | ||
// replace the date fields with the formatted date | ||
dateFieldsNames.forEach((field) => { | ||
if (flattenHit[field]) { | ||
flattenHit[field] = beautifyDate(flattenHit[field]); | ||
} | ||
}); | ||
return flattenHit; | ||
} else { | ||
searchResults = await search(params); | ||
allHits = searchResults.hits.hits; | ||
} | ||
|
||
const resultsFields = fields; | ||
const data = allHits.map(hit => { | ||
// check if the field type is a date | ||
const dateFields = indexPattern.fields.getByType('date'); | ||
const dateFieldsNames = dateFields.map(field => field.name); | ||
const flattenHit = indexPattern.flattenHit(hit); | ||
// replace the date fields with the formatted date | ||
dateFieldsNames.forEach(field => { | ||
if (flattenHit[field]) { | ||
flattenHit[field] = beautifyDate(flattenHit[field]); | ||
} | ||
}); | ||
return flattenHit; | ||
}); | ||
|
||
if (!resultsFields || resultsFields.length === 0){ | ||
return; | ||
} | ||
if (!resultsFields || resultsFields.length === 0) { | ||
return; | ||
} | ||
|
||
if (!data || data.length === 0) | ||
return; | ||
if (!data || data.length === 0) return; | ||
|
||
const parsedData = data.map((row) => { | ||
const parsedRow = resultsFields?.map((field) => { | ||
const value = row[field]; | ||
if (value === undefined || value === null) { | ||
return ''; | ||
} | ||
if (typeof value === 'object') { | ||
return JSON.stringify(value); | ||
} | ||
return `"${value}"`; | ||
}); | ||
return parsedRow?.join(','); | ||
}).join('\n'); | ||
const parsedData = data | ||
.map(row => { | ||
const parsedRow = resultsFields?.map(field => { | ||
const value = row[field]; | ||
if (value === undefined || value === null) { | ||
return ''; | ||
} | ||
if (typeof value === 'object') { | ||
return JSON.stringify(value); | ||
} | ||
return `"${value}"`; | ||
}); | ||
return parsedRow?.join(','); | ||
}) | ||
.join('\n'); | ||
|
||
// create a csv file using blob | ||
const blobData = new Blob( | ||
[ | ||
`${resultsFields?.join(',')}\n${parsedData}` | ||
], | ||
{ type: 'text/csv' } | ||
); | ||
// create a csv file using blob | ||
const blobData = new Blob([`${resultsFields?.join(',')}\n${parsedData}`], { | ||
type: 'text/csv', | ||
}); | ||
|
||
if (blobData) { | ||
// @ts-ignore | ||
FileSaver?.saveAs(blobData, `events-${new Date().toISOString()}.csv`); | ||
} | ||
} | ||
if (blobData) { | ||
// @ts-ignore | ||
FileSaver?.saveAs(blobData, `events-${new Date().toISOString()}.csv`); | ||
} | ||
}; | ||
|
||
export const parseColumns = (fields: IFieldType[]): EuiDataGridColumn[] => { | ||
// remove _source field becuase is a object field and is not supported | ||
fields = fields.filter((field) => field.name !== '_source'); | ||
return fields.map((field) => { | ||
return { | ||
...field, | ||
id: field.name, | ||
display: field.name, | ||
schema: field.type, | ||
actions: { | ||
showHide: true, | ||
}, | ||
}; | ||
}) || []; | ||
} | ||
// remove _source field becuase is a object field and is not supported | ||
fields = fields.filter(field => field.name !== '_source'); | ||
return ( | ||
fields.map(field => { | ||
return { | ||
...field, | ||
id: field.name, | ||
display: field.name, | ||
schema: field.type, | ||
actions: { | ||
showHide: true, | ||
}, | ||
}; | ||
}) || [] | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.