mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Reporting-CSV Export] Re-write CSV Export using SearchSource (#88303)
* [Reporting-CSV Export] Re-write CSV Export using SearchSource * replace PIT solution with scan-and-scroll * update tests * cleanup * simplify pr * update docs * update docs * update telemetry schema * use getSearchRequestBody instead of flatten * Revert "update docs" This reverts commitab9f4d9642
. * optimize some async calls * cleanup * --wip-- [skip ci] * fix telemetry schema * fix telemetry tests * fix snapshot * api docs * api doc updates * use import type * format the data through chains of maps * add another saved search to reporting/ecommerce_kibana * add a failing test * add error logging to query failures * put clear scroll in a finally so the ES error can be captured * log dat error * set dat fieldsFromSource * --wip-- [skip ci] * Revert "add another saved search to reporting/ecommerce_kibana" This reverts commit6edf26eff2
. * functional test fixes * clean up ecommerce test archive * add test for new search with fieldsFromSource set * add tests and refactor tests * cleanup redundant conditionals * add GenerateCsv.getFields * fix some tests * fix double-escaping * fix test snapshots and refactoring * fix other tests * fix test * fix default index pattern in functional tests * fix ts and sort fields when they come from API response * --wip-- [skip ci] * fix formatting and increase maxSizeBytes for testing * remove client-side logic for sanitizing fields * do not prepend timefield name if it already is a column * test the logic to prepend timeField * test the logic to sort the fields * fix functional test * preserve the error from data.search * add functional test for ES returning an error * fix snapshot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
904d98ea59
commit
ec41ae3c49
79 changed files with 6253 additions and 5738 deletions
|
@ -21764,7 +21764,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 245
|
||||
"lineNumber": 246
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21785,7 +21785,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 246
|
||||
"lineNumber": 247
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21806,7 +21806,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 247
|
||||
"lineNumber": 248
|
||||
},
|
||||
"signature": [
|
||||
"({ display: string; val: string; enabled(agg: ",
|
||||
|
@ -21828,7 +21828,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 248
|
||||
"lineNumber": 249
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21849,7 +21849,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 249
|
||||
"lineNumber": 250
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21870,7 +21870,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 250
|
||||
"lineNumber": 251
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21891,7 +21891,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 251
|
||||
"lineNumber": 252
|
||||
},
|
||||
"signature": [
|
||||
"(agg: ",
|
||||
|
@ -21913,7 +21913,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 252
|
||||
"lineNumber": 253
|
||||
},
|
||||
"signature": [
|
||||
"(agg: ",
|
||||
|
@ -21935,7 +21935,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 253
|
||||
"lineNumber": 254
|
||||
},
|
||||
"signature": [
|
||||
"(...types: string[]) => (agg: ",
|
||||
|
@ -21957,7 +21957,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 254
|
||||
"lineNumber": 255
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21978,7 +21978,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 255
|
||||
"lineNumber": 256
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -21999,7 +21999,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 256
|
||||
"lineNumber": 257
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -22010,7 +22010,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 257
|
||||
"lineNumber": 258
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22031,7 +22031,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 258
|
||||
"lineNumber": 259
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22052,7 +22052,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 259
|
||||
"lineNumber": 260
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22073,7 +22073,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 260
|
||||
"lineNumber": 261
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -22084,7 +22084,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 261
|
||||
"lineNumber": 262
|
||||
},
|
||||
"signature": [
|
||||
"string[]"
|
||||
|
@ -22098,7 +22098,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 262
|
||||
"lineNumber": 263
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22119,7 +22119,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 263
|
||||
"lineNumber": 264
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22137,7 +22137,7 @@
|
|||
"label": "aggs",
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 244
|
||||
"lineNumber": 245
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -22148,7 +22148,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 265
|
||||
"lineNumber": 266
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22169,7 +22169,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 266
|
||||
"lineNumber": 267
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22190,7 +22190,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 267
|
||||
"lineNumber": 268
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22211,7 +22211,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 268
|
||||
"lineNumber": 269
|
||||
},
|
||||
"signature": [
|
||||
"typeof ",
|
||||
|
@ -22229,7 +22229,7 @@
|
|||
"label": "search",
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/index.ts",
|
||||
"lineNumber": 243
|
||||
"lineNumber": 244
|
||||
},
|
||||
"initialIsOpen": false
|
||||
},
|
||||
|
|
|
@ -1573,6 +1573,183 @@
|
|||
}
|
||||
],
|
||||
"interfaces": [
|
||||
{
|
||||
"id": "def-server.IScopedSearchClient",
|
||||
"type": "Interface",
|
||||
"label": "IScopedSearchClient",
|
||||
"signature": [
|
||||
{
|
||||
"pluginId": "data",
|
||||
"scope": "server",
|
||||
"docId": "kibDataSearchPluginApi",
|
||||
"section": "def-server.IScopedSearchClient",
|
||||
"text": "IScopedSearchClient"
|
||||
},
|
||||
" extends ",
|
||||
{
|
||||
"pluginId": "data",
|
||||
"scope": "common",
|
||||
"docId": "kibDataSearchPluginApi",
|
||||
"section": "def-common.ISearchClient",
|
||||
"text": "ISearchClient"
|
||||
}
|
||||
],
|
||||
"description": [],
|
||||
"tags": [],
|
||||
"children": [
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.saveSession",
|
||||
"type": "Function",
|
||||
"label": "saveSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 90
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string, attributes: Partial<unknown>) => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "common",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-common.SavedObject",
|
||||
"text": "SavedObject"
|
||||
},
|
||||
"<unknown> | undefined>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.getSession",
|
||||
"type": "Function",
|
||||
"label": "getSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 91
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string) => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "common",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-common.SavedObject",
|
||||
"text": "SavedObject"
|
||||
},
|
||||
"<unknown>>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.findSessions",
|
||||
"type": "Function",
|
||||
"label": "findSessions",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 92
|
||||
},
|
||||
"signature": [
|
||||
"(options: Pick<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCoreSavedObjectsPluginApi",
|
||||
"section": "def-server.SavedObjectsFindOptions",
|
||||
"text": "SavedObjectsFindOptions"
|
||||
},
|
||||
", \"filter\" | \"fields\" | \"searchAfter\" | \"search\" | \"page\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\" | \"pit\">) => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCoreSavedObjectsPluginApi",
|
||||
"section": "def-server.SavedObjectsFindResponse",
|
||||
"text": "SavedObjectsFindResponse"
|
||||
},
|
||||
"<unknown>>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.updateSession",
|
||||
"type": "Function",
|
||||
"label": "updateSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 93
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string, attributes: Partial<unknown>) => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCoreSavedObjectsPluginApi",
|
||||
"section": "def-server.SavedObjectsUpdateResponse",
|
||||
"text": "SavedObjectsUpdateResponse"
|
||||
},
|
||||
"<unknown>>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.cancelSession",
|
||||
"type": "Function",
|
||||
"label": "cancelSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 94
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string) => Promise<{}>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.deleteSession",
|
||||
"type": "Function",
|
||||
"label": "deleteSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 95
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string) => Promise<{}>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tags": [],
|
||||
"id": "def-server.IScopedSearchClient.extendSession",
|
||||
"type": "Function",
|
||||
"label": "extendSession",
|
||||
"description": [],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 96
|
||||
},
|
||||
"signature": [
|
||||
"(sessionId: string, expires: Date) => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCoreSavedObjectsPluginApi",
|
||||
"section": "def-server.SavedObjectsUpdateResponse",
|
||||
"text": "SavedObjectsUpdateResponse"
|
||||
},
|
||||
"<unknown>>"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"path": "src/plugins/data/server/search/types.ts",
|
||||
"lineNumber": 89
|
||||
},
|
||||
"initialIsOpen": false
|
||||
},
|
||||
{
|
||||
"id": "def-server.ISearchSessionService",
|
||||
"type": "Interface",
|
||||
|
|
|
@ -40,6 +40,25 @@
|
|||
"lineNumber": 18
|
||||
},
|
||||
"initialIsOpen": false
|
||||
},
|
||||
{
|
||||
"id": "def-public.loadSharingDataHelpers",
|
||||
"type": "Function",
|
||||
"label": "loadSharingDataHelpers",
|
||||
"signature": [
|
||||
"() => Promise<typeof ",
|
||||
"src/plugins/discover/public/application/helpers/get_sharing_data",
|
||||
">"
|
||||
],
|
||||
"description": [],
|
||||
"children": [],
|
||||
"tags": [],
|
||||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "src/plugins/discover/public/shared/index.ts",
|
||||
"lineNumber": 12
|
||||
},
|
||||
"initialIsOpen": false
|
||||
}
|
||||
],
|
||||
"interfaces": [
|
||||
|
|
|
@ -851,7 +851,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 65
|
||||
"lineNumber": 69
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -873,7 +873,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 65
|
||||
"lineNumber": 69
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -881,7 +881,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 65
|
||||
"lineNumber": 69
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -917,7 +917,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 75
|
||||
"lineNumber": 79
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -925,7 +925,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 75
|
||||
"lineNumber": 79
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -961,7 +961,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 89
|
||||
"lineNumber": 93
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -969,7 +969,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 89
|
||||
"lineNumber": 93
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -985,7 +985,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 102
|
||||
"lineNumber": 106
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1001,7 +1001,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 113
|
||||
"lineNumber": 117
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1017,7 +1017,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 120
|
||||
"lineNumber": 124
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1053,7 +1053,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 127
|
||||
"lineNumber": 131
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1061,7 +1061,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 127
|
||||
"lineNumber": 131
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1079,7 +1079,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 135
|
||||
"lineNumber": 139
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1102,7 +1102,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 155
|
||||
"lineNumber": 159
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1126,7 +1126,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 165
|
||||
"lineNumber": 169
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1149,7 +1149,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 173
|
||||
"lineNumber": 177
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1210,7 +1210,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 177
|
||||
"lineNumber": 181
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1218,7 +1218,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 177
|
||||
"lineNumber": 181
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1242,7 +1242,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 181
|
||||
"lineNumber": 185
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1266,7 +1266,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 185
|
||||
"lineNumber": 189
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1290,7 +1290,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 195
|
||||
"lineNumber": 199
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1313,7 +1313,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 204
|
||||
"lineNumber": 208
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1336,7 +1336,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 211
|
||||
"lineNumber": 216
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1382,7 +1382,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 220
|
||||
"lineNumber": 225
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1390,7 +1390,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 220
|
||||
"lineNumber": 225
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1435,7 +1435,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 226
|
||||
"lineNumber": 231
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1454,7 +1454,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 226
|
||||
"lineNumber": 231
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1462,7 +1462,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 226
|
||||
"lineNumber": 231
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1500,7 +1500,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 240
|
||||
"lineNumber": 245
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1513,7 +1513,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 240
|
||||
"lineNumber": 245
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1532,7 +1532,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 240
|
||||
"lineNumber": 245
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1540,7 +1540,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 240
|
||||
"lineNumber": 245
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1593,7 +1593,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 260
|
||||
"lineNumber": 265
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1612,7 +1612,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 260
|
||||
"lineNumber": 265
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1620,7 +1620,55 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 260
|
||||
"lineNumber": 265
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "def-server.ReportingCore.getDataService",
|
||||
"type": "Function",
|
||||
"label": "getDataService",
|
||||
"signature": [
|
||||
"() => Promise<",
|
||||
{
|
||||
"pluginId": "data",
|
||||
"scope": "server",
|
||||
"docId": "kibDataPluginApi",
|
||||
"section": "def-server.DataPluginStart",
|
||||
"text": "DataPluginStart"
|
||||
},
|
||||
">"
|
||||
],
|
||||
"description": [],
|
||||
"children": [],
|
||||
"tags": [],
|
||||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 275
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "def-server.ReportingCore.getEsClient",
|
||||
"type": "Function",
|
||||
"label": "getEsClient",
|
||||
"signature": [
|
||||
"() => Promise<",
|
||||
{
|
||||
"pluginId": "core",
|
||||
"scope": "server",
|
||||
"docId": "kibCorePluginApi",
|
||||
"section": "def-server.IClusterClient",
|
||||
"text": "IClusterClient"
|
||||
},
|
||||
">"
|
||||
],
|
||||
"description": [],
|
||||
"children": [],
|
||||
"tags": [],
|
||||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 280
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1642,7 +1690,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 270
|
||||
"lineNumber": 285
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1650,7 +1698,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 270
|
||||
"lineNumber": 285
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1672,7 +1720,7 @@
|
|||
"description": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 274
|
||||
"lineNumber": 289
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -1680,7 +1728,7 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 274
|
||||
"lineNumber": 289
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -1696,13 +1744,13 @@
|
|||
"returnComment": [],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 278
|
||||
"lineNumber": 293
|
||||
}
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"path": "x-pack/plugins/reporting/server/core.ts",
|
||||
"lineNumber": 54
|
||||
"lineNumber": 58
|
||||
},
|
||||
"initialIsOpen": false
|
||||
},
|
||||
|
|
|
@ -97,8 +97,7 @@ export const getTopNavLinks = ({
|
|||
const sharingData = await getSharingData(
|
||||
searchSource,
|
||||
state.appStateContainer.getState(),
|
||||
services.uiSettings,
|
||||
getFieldCounts
|
||||
services.uiSettings
|
||||
);
|
||||
services.share.toggleShareContextMenu({
|
||||
anchorElement,
|
||||
|
|
|
@ -11,59 +11,130 @@ import { getSharingData, showPublicUrlSwitch } from './get_sharing_data';
|
|||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks';
|
||||
import { indexPatternMock } from '../../__mocks__/index_pattern';
|
||||
import { SORT_DEFAULT_ORDER_SETTING } from '../../../common';
|
||||
import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common';
|
||||
import { IndexPattern } from 'src/plugins/data/public';
|
||||
|
||||
describe('getSharingData', () => {
|
||||
let mockConfig: IUiSettingsClient;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig = ({
|
||||
get: (key: string) => {
|
||||
if (key === SORT_DEFAULT_ORDER_SETTING) {
|
||||
return 'desc';
|
||||
}
|
||||
if (key === DOC_HIDE_TIME_COLUMN_SETTING) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
} as unknown) as IUiSettingsClient;
|
||||
});
|
||||
|
||||
test('returns valid data for sharing', async () => {
|
||||
const searchSourceMock = createSearchSourceMock({ index: indexPatternMock });
|
||||
const result = await getSharingData(searchSourceMock, { columns: [] }, mockConfig);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"searchSource": Object {
|
||||
"index": "the-index-pattern-id",
|
||||
"sort": Array [
|
||||
Object {
|
||||
"_score": "desc",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('fields have prepended timeField', async () => {
|
||||
const index = { ...indexPatternMock } as IndexPattern;
|
||||
index.timeFieldName = 'cool-timefield';
|
||||
|
||||
const searchSourceMock = createSearchSourceMock({ index });
|
||||
const result = await getSharingData(
|
||||
searchSourceMock,
|
||||
{ columns: [] },
|
||||
({
|
||||
get: (key: string) => {
|
||||
if (key === SORT_DEFAULT_ORDER_SETTING) {
|
||||
return 'desc';
|
||||
}
|
||||
return false;
|
||||
},
|
||||
} as unknown) as IUiSettingsClient,
|
||||
() => Promise.resolve({})
|
||||
{
|
||||
columns: [
|
||||
'cool-field-1',
|
||||
'cool-field-2',
|
||||
'cool-field-3',
|
||||
'cool-field-4',
|
||||
'cool-field-5',
|
||||
'cool-field-6',
|
||||
],
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [],
|
||||
"indexPatternId": "the-index-pattern-id",
|
||||
"metaFields": Array [
|
||||
"_index",
|
||||
"_score",
|
||||
],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {},
|
||||
"fields": Array [],
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
"searchSource": Object {
|
||||
"fields": Array [
|
||||
"cool-timefield",
|
||||
"cool-field-1",
|
||||
"cool-field-2",
|
||||
"cool-field-3",
|
||||
"cool-field-4",
|
||||
"cool-field-5",
|
||||
"cool-field-6",
|
||||
],
|
||||
"index": "the-index-pattern-id",
|
||||
"sort": Array [
|
||||
Object {
|
||||
"_doc": "desc",
|
||||
},
|
||||
"runtime_mappings": Object {},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [
|
||||
Object {
|
||||
"_score": Object {
|
||||
"order": "desc",
|
||||
},
|
||||
},
|
||||
],
|
||||
"stored_fields": Array [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"index": "the-index-pattern-title",
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('fields conditionally do not have prepended timeField', async () => {
|
||||
mockConfig = ({
|
||||
get: (key: string) => {
|
||||
if (key === DOC_HIDE_TIME_COLUMN_SETTING) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
} as unknown) as IUiSettingsClient;
|
||||
|
||||
const index = { ...indexPatternMock } as IndexPattern;
|
||||
index.timeFieldName = 'cool-timefield';
|
||||
|
||||
const searchSourceMock = createSearchSourceMock({ index });
|
||||
const result = await getSharingData(
|
||||
searchSourceMock,
|
||||
{
|
||||
columns: [
|
||||
'cool-field-1',
|
||||
'cool-field-2',
|
||||
'cool-field-3',
|
||||
'cool-field-4',
|
||||
'cool-field-5',
|
||||
'cool-field-6',
|
||||
],
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"searchSource": Object {
|
||||
"fields": Array [
|
||||
"cool-field-1",
|
||||
"cool-field-2",
|
||||
"cool-field-3",
|
||||
"cool-field-4",
|
||||
"cool-field-5",
|
||||
"cool-field-6",
|
||||
],
|
||||
"index": "the-index-pattern-id",
|
||||
"sort": Array [
|
||||
Object {
|
||||
"_doc": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -6,57 +6,28 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import type { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common';
|
||||
import { getSortForSearchSource } from '../angular/doc_table';
|
||||
import { ISearchSource } from '../../../../data/common';
|
||||
import { AppState } from '../angular/discover_state';
|
||||
import { SortOrder } from '../../saved_searches/types';
|
||||
|
||||
const getSharingDataFields = async (
|
||||
getFieldCounts: () => Promise<Record<string, number>>,
|
||||
selectedFields: string[],
|
||||
timeFieldName: string,
|
||||
hideTimeColumn: boolean
|
||||
) => {
|
||||
if (
|
||||
selectedFields.length === 0 ||
|
||||
(selectedFields.length === 1 && selectedFields[0] === '_source')
|
||||
) {
|
||||
const fieldCounts = await getFieldCounts();
|
||||
return {
|
||||
searchFields: undefined,
|
||||
selectFields: Object.keys(fieldCounts).sort(),
|
||||
};
|
||||
}
|
||||
|
||||
const fields =
|
||||
timeFieldName && !hideTimeColumn ? [timeFieldName, ...selectedFields] : selectedFields;
|
||||
return {
|
||||
searchFields: fields,
|
||||
selectFields: fields,
|
||||
};
|
||||
};
|
||||
import type { SavedSearch, SortOrder } from '../../saved_searches/types';
|
||||
|
||||
/**
|
||||
* Preparing data to share the current state as link or CSV/Report
|
||||
*/
|
||||
export async function getSharingData(
|
||||
currentSearchSource: ISearchSource,
|
||||
state: AppState,
|
||||
config: IUiSettingsClient,
|
||||
getFieldCounts: () => Promise<Record<string, number>>
|
||||
state: AppState | SavedSearch,
|
||||
config: IUiSettingsClient
|
||||
) {
|
||||
const searchSource = currentSearchSource.createCopy();
|
||||
const index = searchSource.getField('index')!;
|
||||
const fields = {
|
||||
fields: searchSource.getField('fields'),
|
||||
fieldsFromSource: searchSource.getField('fieldsFromSource'),
|
||||
};
|
||||
|
||||
const { searchFields, selectFields } = await getSharingDataFields(
|
||||
getFieldCounts,
|
||||
state.columns || [],
|
||||
index.timeFieldName || '',
|
||||
config.get(DOC_HIDE_TIME_COLUMN_SETTING)
|
||||
);
|
||||
searchSource.setField('fieldsFromSource', searchFields);
|
||||
searchSource.setField(
|
||||
'sort',
|
||||
getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING))
|
||||
|
@ -66,17 +37,27 @@ export async function getSharingData(
|
|||
searchSource.removeField('aggs');
|
||||
searchSource.removeField('size');
|
||||
|
||||
const body = await searchSource.getSearchRequestBody();
|
||||
// fields get re-set to match the saved search columns
|
||||
let columns = state.columns || [];
|
||||
|
||||
if (columns && columns.length > 0) {
|
||||
// conditionally add the time field column:
|
||||
let timeFieldName: string | undefined;
|
||||
const hideTimeColumn = config.get(DOC_HIDE_TIME_COLUMN_SETTING);
|
||||
if (!hideTimeColumn && index && index.timeFieldName) {
|
||||
timeFieldName = index.timeFieldName;
|
||||
}
|
||||
if (timeFieldName && !columns.includes(timeFieldName)) {
|
||||
columns = [timeFieldName, ...columns];
|
||||
}
|
||||
|
||||
// if columns were selected in the saved search, use them for the searchSource's fields
|
||||
const fieldsKey = fields.fieldsFromSource ? 'fieldsFromSource' : 'fields';
|
||||
searchSource.setField(fieldsKey, columns);
|
||||
}
|
||||
|
||||
return {
|
||||
searchRequest: {
|
||||
index: index.title,
|
||||
body,
|
||||
},
|
||||
fields: selectFields,
|
||||
metaFields: index.metaFields,
|
||||
conflictedTypesFields: index.fields.filter((f) => f.type === 'conflict').map((f) => f.name),
|
||||
indexPatternId: index.id,
|
||||
searchSource: searchSource.getSerializedFields(true),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ export const KBN_SCREENSHOT_HEADER_BLOCK_LIST_STARTS_WITH_PATTERN = ['proxy-'];
|
|||
export const UI_SETTINGS_CUSTOM_PDF_LOGO = 'xpackReporting:customPdfLogo';
|
||||
export const UI_SETTINGS_CSV_SEPARATOR = 'csv:separator';
|
||||
export const UI_SETTINGS_CSV_QUOTE_VALUES = 'csv:quoteValues';
|
||||
export const UI_SETTINGS_DATEFORMAT_TZ = 'dateFormat:tz';
|
||||
|
||||
export const LAYOUT_TYPES = {
|
||||
PRESERVE_LAYOUT: 'preserve_layout',
|
||||
|
@ -57,13 +58,16 @@ export const LAYOUT_TYPES = {
|
|||
};
|
||||
|
||||
// Export Type Definitions
|
||||
export const CSV_REPORT_TYPE = 'CSV';
|
||||
export const CSV_JOB_TYPE = 'csv_searchsource';
|
||||
|
||||
export const PDF_REPORT_TYPE = 'printablePdf';
|
||||
export const PDF_JOB_TYPE = 'printable_pdf';
|
||||
|
||||
export const PNG_REPORT_TYPE = 'PNG';
|
||||
export const PNG_JOB_TYPE = 'PNG';
|
||||
|
||||
export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject';
|
||||
export const CSV_SEARCHSOURCE_IMMEDIATE_TYPE = 'csv_searchsource_immediate';
|
||||
|
||||
// This is deprecated because it lacks support for runtime fields
|
||||
// but the extension points are still needed for pre-existing scripted automation, until 8.0
|
||||
|
@ -86,9 +90,9 @@ export const API_BASE_GENERATE = `${API_BASE_URL}/generate`;
|
|||
export const API_LIST_URL = `${API_BASE_URL}/jobs`;
|
||||
export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`;
|
||||
|
||||
// hacky endpoint
|
||||
// hacky endpoint: download CSV without queueing a report
|
||||
export const API_BASE_URL_V1 = '/api/reporting/v1'; //
|
||||
export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv/saved-object`;
|
||||
export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv_searchsource`;
|
||||
|
||||
// Management UI route
|
||||
export const REPORTING_MANAGEMENT_HOME = '/app/management/insightsAndAlerting/reporting';
|
||||
|
|
|
@ -47,9 +47,10 @@ export interface ReportDocumentHead {
|
|||
export interface TaskRunResult {
|
||||
content_type: string | null;
|
||||
content: string | null;
|
||||
csv_contains_formulas?: boolean;
|
||||
size: number;
|
||||
csv_contains_formulas?: boolean;
|
||||
max_size_reached?: boolean;
|
||||
needs_sorting?: boolean;
|
||||
warnings?: string[];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,7 @@ import React, { Component, ReactElement } from 'react';
|
|||
import { ToastsSetup } from 'src/core/public';
|
||||
import url from 'url';
|
||||
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
CSV_REPORT_TYPE_DEPRECATED,
|
||||
PDF_REPORT_TYPE,
|
||||
PNG_REPORT_TYPE,
|
||||
} from '../../common/constants';
|
||||
import { CSV_REPORT_TYPE, PDF_REPORT_TYPE, PNG_REPORT_TYPE } from '../../common/constants';
|
||||
import { BaseParams } from '../../common/types';
|
||||
import { ReportingAPIClient } from '../lib/reporting_api_client';
|
||||
|
||||
|
@ -177,8 +173,8 @@ class ReportingPanelContentUi extends Component<Props, State> {
|
|||
switch (this.props.reportType) {
|
||||
case PDF_REPORT_TYPE:
|
||||
return 'PDF';
|
||||
case 'csv':
|
||||
return CSV_REPORT_TYPE_DEPRECATED;
|
||||
case 'csv_searchsource':
|
||||
return CSV_REPORT_TYPE;
|
||||
case 'png':
|
||||
return PNG_REPORT_TYPE;
|
||||
default:
|
||||
|
|
|
@ -52,7 +52,20 @@ describe('GetCsvReportPanelAction', () => {
|
|||
context = {
|
||||
embeddable: {
|
||||
type: 'search',
|
||||
getSavedSearch: () => ({ id: 'lebowski' }),
|
||||
getSavedSearch: () => {
|
||||
const searchSource = {
|
||||
createCopy: () => searchSource,
|
||||
removeField: jest.fn(),
|
||||
setField: jest.fn(),
|
||||
getField: jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'index') {
|
||||
return 'my-test-index-*';
|
||||
}
|
||||
}),
|
||||
getSerializedFields: jest.fn().mockImplementation(() => ({})),
|
||||
};
|
||||
return { searchSource };
|
||||
},
|
||||
getTitle: () => `The Dude`,
|
||||
getInspectorAdapters: () => null,
|
||||
getInput: () => ({
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import dateMath from '@elastic/datemath';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment-timezone';
|
||||
import { CoreSetup } from 'src/core/public';
|
||||
import {
|
||||
loadSharingDataHelpers,
|
||||
ISearchEmbeddable,
|
||||
SavedSearch,
|
||||
SEARCH_EMBEDDABLE_TYPE,
|
||||
} from '../../../../../src/plugins/discover/public';
|
||||
import { IEmbeddable, ViewMode } from '../../../../../src/plugins/embeddable/public';
|
||||
|
@ -21,6 +21,7 @@ import {
|
|||
} from '../../../../../src/plugins/ui_actions/public';
|
||||
import { LicensingPluginSetup } from '../../../licensing/public';
|
||||
import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../common/constants';
|
||||
import { JobParamsDownloadCSV } from '../../server/export_types/csv_searchsource_immediate/types';
|
||||
import { checkLicense } from '../lib/license_check';
|
||||
|
||||
function isSavedSearchEmbeddable(
|
||||
|
@ -61,17 +62,16 @@ export class GetCsvReportPanelAction implements ActionDefinition<ActionContext>
|
|||
});
|
||||
}
|
||||
|
||||
public getSearchRequestBody({ searchEmbeddable }: { searchEmbeddable: any }) {
|
||||
const adapters = searchEmbeddable.getInspectorAdapters();
|
||||
if (!adapters) {
|
||||
return {};
|
||||
}
|
||||
public async getSearchSource(savedSearch: SavedSearch, embeddable: ISearchEmbeddable) {
|
||||
const { getSharingData } = await loadSharingDataHelpers();
|
||||
const searchSource = savedSearch.searchSource.createCopy();
|
||||
const { searchSource: serializedSearchSource } = await getSharingData(
|
||||
searchSource,
|
||||
savedSearch, // TODO: get unsaved state (using embeddale.searchScope): https://github.com/elastic/kibana/issues/43977
|
||||
this.core.uiSettings
|
||||
);
|
||||
|
||||
if (adapters.requests.requests.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return searchEmbeddable.getSavedSearch().searchSource.getSearchRequestBody();
|
||||
return serializedSearchSource;
|
||||
}
|
||||
|
||||
public isCompatible = async (context: ActionContext) => {
|
||||
|
@ -95,34 +95,18 @@ export class GetCsvReportPanelAction implements ActionDefinition<ActionContext>
|
|||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
timeRange: { to, from },
|
||||
} = embeddable.getInput();
|
||||
const savedSearch = embeddable.getSavedSearch();
|
||||
const searchSource = await this.getSearchSource(savedSearch, embeddable);
|
||||
|
||||
const searchEmbeddable = embeddable;
|
||||
const searchRequestBody = await this.getSearchRequestBody({ searchEmbeddable });
|
||||
const state = _.pick(searchRequestBody, ['sort', 'docvalue_fields', 'query']);
|
||||
const kibanaTimezone = this.core.uiSettings.get('dateFormat:tz');
|
||||
const browserTimezone = kibanaTimezone === 'Browser' ? moment.tz.guess() : kibanaTimezone;
|
||||
const immediateJobParams: JobParamsDownloadCSV = {
|
||||
searchSource,
|
||||
browserTimezone,
|
||||
title: savedSearch.title,
|
||||
};
|
||||
|
||||
const id = `search:${embeddable.getSavedSearch().id}`;
|
||||
const timezone = kibanaTimezone === 'Browser' ? moment.tz.guess() : kibanaTimezone;
|
||||
const fromTime = dateMath.parse(from);
|
||||
const toTime = dateMath.parse(to, { roundUp: true });
|
||||
|
||||
if (!fromTime || !toTime) {
|
||||
return this.onGenerationFail(
|
||||
new Error(`Invalid time range: From: ${fromTime}, To: ${toTime}`)
|
||||
);
|
||||
}
|
||||
|
||||
const body = JSON.stringify({
|
||||
timerange: {
|
||||
min: fromTime.format(),
|
||||
max: toTime.format(),
|
||||
timezone,
|
||||
},
|
||||
state,
|
||||
});
|
||||
const body = JSON.stringify(immediateJobParams);
|
||||
|
||||
this.isDownloading = true;
|
||||
|
||||
|
@ -137,11 +121,11 @@ export class GetCsvReportPanelAction implements ActionDefinition<ActionContext>
|
|||
});
|
||||
|
||||
await this.core.http
|
||||
.post(`${API_GENERATE_IMMEDIATE}/${id}`, { body })
|
||||
.post(`${API_GENERATE_IMMEDIATE}`, { body })
|
||||
.then((rawResponse: string) => {
|
||||
this.isDownloading = false;
|
||||
|
||||
const download = `${embeddable.getSavedSearch().title}.csv`;
|
||||
const download = `${savedSearch.title}.csv`;
|
||||
const blob = new Blob([rawResponse], { type: 'text/csv;charset=utf-8;' });
|
||||
|
||||
// Hack for IE11 Support
|
||||
|
|
|
@ -11,10 +11,8 @@ import React from 'react';
|
|||
import { IUiSettingsClient, ToastsSetup } from 'src/core/public';
|
||||
import { ShareContext } from '../../../../../src/plugins/share/public';
|
||||
import { LicensingPluginSetup } from '../../../licensing/public';
|
||||
import {
|
||||
JobParamsDeprecatedCSV,
|
||||
SearchRequestDeprecatedCSV,
|
||||
} from '../../server/export_types/csv/types';
|
||||
import { CSV_JOB_TYPE } from '../../common/constants';
|
||||
import { JobParamsCSV } from '../../server/export_types/csv_searchsource/types';
|
||||
import { ReportingPanelContent } from '../components/reporting_panel_content_lazy';
|
||||
import { checkLicense } from '../lib/license_check';
|
||||
import { ReportingAPIClient } from '../lib/reporting_api_client';
|
||||
|
@ -56,22 +54,18 @@ export const csvReportingProvider = ({
|
|||
objectType,
|
||||
objectId,
|
||||
sharingData,
|
||||
isDirty,
|
||||
onClose,
|
||||
isDirty,
|
||||
}: ShareContext) => {
|
||||
if ('search' !== objectType) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const jobParams: JobParamsDeprecatedCSV = {
|
||||
const jobParams: JobParamsCSV = {
|
||||
browserTimezone,
|
||||
objectType,
|
||||
title: sharingData.title as string,
|
||||
indexPatternId: sharingData.indexPatternId as string,
|
||||
searchRequest: sharingData.searchRequest as SearchRequestDeprecatedCSV,
|
||||
fields: sharingData.fields as string[],
|
||||
metaFields: sharingData.metaFields as string[],
|
||||
conflictedTypesFields: sharingData.conflictedTypesFields as string[],
|
||||
objectType,
|
||||
searchSource: sharingData.searchSource,
|
||||
};
|
||||
|
||||
const getJobParams = () => jobParams;
|
||||
|
@ -99,7 +93,7 @@ export const csvReportingProvider = ({
|
|||
<ReportingPanelContent
|
||||
apiClient={apiClient}
|
||||
toasts={toasts}
|
||||
reportType="csv"
|
||||
reportType={CSV_JOB_TYPE}
|
||||
layoutId={undefined}
|
||||
objectId={objectId}
|
||||
getJobParams={getJobParams}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { first, map, take } from 'rxjs/operators';
|
|||
import {
|
||||
BasePath,
|
||||
ElasticsearchServiceSetup,
|
||||
IClusterClient,
|
||||
KibanaRequest,
|
||||
PluginInitializerContext,
|
||||
SavedObjectsClientContract,
|
||||
|
@ -31,6 +32,7 @@ import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/scr
|
|||
import { ReportingStore } from './lib/store';
|
||||
import { ExecuteReportTask, MonitorReportsTask, ReportTaskParams } from './lib/tasks';
|
||||
import { ReportingPluginRouter } from './types';
|
||||
import { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server';
|
||||
|
||||
export interface ReportingInternalSetup {
|
||||
basePath: Pick<BasePath, 'set'>;
|
||||
|
@ -48,6 +50,8 @@ export interface ReportingInternalStart {
|
|||
store: ReportingStore;
|
||||
savedObjects: SavedObjectsServiceStart;
|
||||
uiSettings: UiSettingsServiceStart;
|
||||
esClient: IClusterClient;
|
||||
data: DataPluginStart;
|
||||
taskManager: TaskManagerStartContract;
|
||||
}
|
||||
|
||||
|
@ -208,6 +212,7 @@ export class ReportingCore {
|
|||
return this.pluginSetupDeps;
|
||||
}
|
||||
|
||||
// NOTE: Uses the Legacy API
|
||||
public getElasticsearchService() {
|
||||
return this.getPluginSetupDeps().elasticsearch;
|
||||
}
|
||||
|
@ -267,6 +272,16 @@ export class ReportingCore {
|
|||
return await this.getUiSettingsServiceFactory(savedObjectsClient);
|
||||
}
|
||||
|
||||
public async getDataService() {
|
||||
const startDeps = await this.getPluginStartDeps();
|
||||
return startDeps.data;
|
||||
}
|
||||
|
||||
public async getEsClient() {
|
||||
const startDeps = await this.getPluginStartDeps();
|
||||
return startDeps.esClient;
|
||||
}
|
||||
|
||||
public trackReport(reportId: string) {
|
||||
this.executing.add(reportId);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ export { omitBlockedHeaders } from './omit_blocked_headers';
|
|||
export { validateUrls } from './validate_urls';
|
||||
|
||||
export interface TimeRangeParams {
|
||||
timezone: string;
|
||||
min?: Date | string | number | null;
|
||||
max?: Date | string | number | null;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { pick, keys, values, some } from 'lodash';
|
||||
import { cellHasFormulas } from './cell_has_formula';
|
||||
import { cellHasFormulas } from '../../csv_searchsource/generate_csv/cell_has_formula';
|
||||
|
||||
interface IFlattened {
|
||||
[header: string]: string;
|
||||
|
|
|
@ -13,15 +13,18 @@ import { CSV_BOM_CHARS } from '../../../../common/constants';
|
|||
import { byteSizeValueToNumber } from '../../../../common/schema_utils';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { getFieldFormats } from '../../../services';
|
||||
import { IndexPatternSavedObjectDeprecatedCSV, SavedSearchGeneratorResult } from '../types';
|
||||
import { createEscapeValue } from '../../csv_searchsource/generate_csv/escape_value';
|
||||
import { MaxSizeStringBuilder } from '../../csv_searchsource/generate_csv/max_size_string_builder';
|
||||
import {
|
||||
IndexPatternSavedObjectDeprecatedCSV,
|
||||
SavedSearchGeneratorResultDeprecatedCSV,
|
||||
} from '../types';
|
||||
import { checkIfRowsHaveFormulas } from './check_cells_for_formulas';
|
||||
import { createEscapeValue } from './escape_value';
|
||||
import { fieldFormatMapFactory } from './field_format_map';
|
||||
import { createFlattenHit } from './flatten_hit';
|
||||
import { createFormatCsvValues } from './format_csv_values';
|
||||
import { getUiSettings } from './get_ui_settings';
|
||||
import { createHitIterator, EndpointCaller } from './hit_iterator';
|
||||
import { MaxSizeStringBuilder } from './max_size_string_builder';
|
||||
|
||||
interface SearchRequest {
|
||||
index: string;
|
||||
|
@ -55,7 +58,7 @@ export function createGenerateCsv(logger: LevelLogger) {
|
|||
uiSettingsClient: IUiSettingsClient,
|
||||
callEndpoint: EndpointCaller,
|
||||
cancellationToken: CancellationToken
|
||||
): Promise<SavedSearchGeneratorResult> {
|
||||
): Promise<SavedSearchGeneratorResultDeprecatedCSV> {
|
||||
const settings = await getUiSettings(job.browserTimezone, uiSettingsClient, config, logger);
|
||||
const escapeValue = createEscapeValue(settings.quoteValues, settings.escapeFormulaValues);
|
||||
const bom = config.get('csv', 'useByteOrderMarkEncoding') ? CSV_BOM_CHARS : '';
|
||||
|
|
|
@ -77,15 +77,10 @@ type FormatsMapDeprecatedCSV = Map<
|
|||
}
|
||||
>;
|
||||
|
||||
export interface SavedSearchGeneratorResult {
|
||||
export interface SavedSearchGeneratorResultDeprecatedCSV {
|
||||
content: string;
|
||||
size: number;
|
||||
maxSizeReached: boolean;
|
||||
csvContainsFormulas?: boolean;
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface CsvResultFromSearch {
|
||||
type: string;
|
||||
result: SavedSearchGeneratorResult;
|
||||
}
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { notFound, notImplemented } from '@hapi/boom';
|
||||
import { get } from 'lodash';
|
||||
import { CsvFromSavedObjectRequest } from '../../routes/generate_from_savedobject_immediate';
|
||||
import type { ReportingRequestHandlerContext } from '../../types';
|
||||
import { CreateJobFnFactory } from '../../types';
|
||||
import {
|
||||
JobParamsPanelCsv,
|
||||
JobPayloadPanelCsv,
|
||||
SavedObject,
|
||||
SavedObjectReference,
|
||||
SavedObjectServiceError,
|
||||
VisObjectAttributesJSON,
|
||||
} from './types';
|
||||
|
||||
export type ImmediateCreateJobFn = (
|
||||
jobParams: JobParamsPanelCsv,
|
||||
context: ReportingRequestHandlerContext,
|
||||
req: CsvFromSavedObjectRequest
|
||||
) => Promise<JobPayloadPanelCsv>;
|
||||
|
||||
export const createJobFnFactory: CreateJobFnFactory<ImmediateCreateJobFn> = function createJobFactoryFn(
|
||||
reporting,
|
||||
parentLogger
|
||||
) {
|
||||
const logger = parentLogger.clone(['create-job']);
|
||||
|
||||
return async function createJob(jobParams, context, req) {
|
||||
const { savedObjectType, savedObjectId } = jobParams;
|
||||
|
||||
const panel = await Promise.resolve()
|
||||
.then(() => context.core.savedObjects.client.get(savedObjectType, savedObjectId))
|
||||
.then(async (savedObject: SavedObject) => {
|
||||
const { attributes, references } = savedObject;
|
||||
const { kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON } = attributes;
|
||||
const { timerange } = req.body;
|
||||
|
||||
if (!kibanaSavedObjectMetaJSON) {
|
||||
throw new Error('Could not parse saved object data!');
|
||||
}
|
||||
|
||||
const kibanaSavedObjectMeta = {
|
||||
...kibanaSavedObjectMetaJSON,
|
||||
searchSource: JSON.parse(kibanaSavedObjectMetaJSON.searchSourceJSON),
|
||||
};
|
||||
|
||||
const { visState: visStateJSON } = attributes as VisObjectAttributesJSON;
|
||||
if (visStateJSON) {
|
||||
throw notImplemented('Visualization types are not yet implemented');
|
||||
}
|
||||
|
||||
// saved search type
|
||||
const { searchSource } = kibanaSavedObjectMeta;
|
||||
if (!searchSource || !references) {
|
||||
throw new Error('The saved search object is missing configuration fields!');
|
||||
}
|
||||
|
||||
const indexPatternMeta = references.find(
|
||||
(ref: SavedObjectReference) => ref.type === 'index-pattern'
|
||||
);
|
||||
if (!indexPatternMeta) {
|
||||
throw new Error('Could not find index pattern for the saved search!');
|
||||
}
|
||||
|
||||
return {
|
||||
attributes: {
|
||||
...attributes,
|
||||
kibanaSavedObjectMeta: { searchSource },
|
||||
},
|
||||
indexPatternSavedObjectId: indexPatternMeta.id,
|
||||
timerange,
|
||||
};
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
const boomErr = (err as unknown) as { isBoom: boolean };
|
||||
if (boomErr.isBoom) {
|
||||
throw err;
|
||||
}
|
||||
const errPayload: SavedObjectServiceError = get(err, 'output.payload', { statusCode: 0 });
|
||||
if (errPayload.statusCode === 404) {
|
||||
throw notFound(errPayload.message);
|
||||
}
|
||||
logger.error(err);
|
||||
throw new Error(`Unable to create a job from saved object data! Error: ${err}`);
|
||||
});
|
||||
|
||||
return { ...jobParams, panel };
|
||||
};
|
||||
};
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaRequest } from 'src/core/server';
|
||||
import { CancellationToken } from '../../../common';
|
||||
import { CONTENT_TYPE_CSV } from '../../../common/constants';
|
||||
import { TaskRunResult } from '../../lib/tasks';
|
||||
import type { ReportingRequestHandlerContext } from '../../types';
|
||||
import { RunTaskFnFactory } from '../../types';
|
||||
import { createGenerateCsv } from '../csv/generate_csv';
|
||||
import { getGenerateCsvParams } from './lib/get_csv_job';
|
||||
import { JobPayloadPanelCsv } from './types';
|
||||
|
||||
/*
|
||||
* ImmediateExecuteFn receives the job doc payload because the payload was
|
||||
* generated in the ScheduleFn
|
||||
*/
|
||||
export type ImmediateExecuteFn = (
|
||||
jobId: null,
|
||||
job: JobPayloadPanelCsv,
|
||||
context: ReportingRequestHandlerContext,
|
||||
req: KibanaRequest
|
||||
) => Promise<TaskRunResult>;
|
||||
|
||||
export const runTaskFnFactory: RunTaskFnFactory<ImmediateExecuteFn> = function executeJobFactoryFn(
|
||||
reporting,
|
||||
parentLogger
|
||||
) {
|
||||
const config = reporting.getConfig();
|
||||
const logger = parentLogger.clone(['execute-job']);
|
||||
|
||||
return async function runTask(jobId, jobPayload, context, req) {
|
||||
const generateCsv = createGenerateCsv(logger);
|
||||
const { panel } = jobPayload;
|
||||
|
||||
logger.debug(`Execute job generating saved search CSV`);
|
||||
|
||||
const savedObjectsClient = context.core.savedObjects.client;
|
||||
const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient);
|
||||
const job = await getGenerateCsvParams(
|
||||
jobPayload,
|
||||
panel,
|
||||
savedObjectsClient,
|
||||
uiSettingsClient,
|
||||
logger
|
||||
);
|
||||
|
||||
const elasticsearch = reporting.getElasticsearchService();
|
||||
const { callAsCurrentUser } = elasticsearch.legacy.client.asScoped(req);
|
||||
|
||||
const { content, maxSizeReached, size, csvContainsFormulas, warnings } = await generateCsv(
|
||||
job,
|
||||
config,
|
||||
uiSettingsClient,
|
||||
callAsCurrentUser,
|
||||
new CancellationToken() // can not be cancelled
|
||||
);
|
||||
|
||||
if (csvContainsFormulas) {
|
||||
logger.warn(`CSV may contain formulas whose values have been escaped`);
|
||||
}
|
||||
|
||||
if (maxSizeReached) {
|
||||
logger.warn(`Max size reached: CSV output truncated to ${size} bytes`);
|
||||
}
|
||||
|
||||
return {
|
||||
content_type: CONTENT_TYPE_CSV,
|
||||
content,
|
||||
max_size_reached: maxSizeReached,
|
||||
size,
|
||||
csv_contains_formulas: csvContainsFormulas,
|
||||
warnings,
|
||||
};
|
||||
};
|
||||
};
|
|
@ -1,340 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createMockLevelLogger } from '../../../test_helpers';
|
||||
import { JobParamsPanelCsv, SearchPanel } from '../types';
|
||||
import { getGenerateCsvParams } from './get_csv_job';
|
||||
|
||||
const logger = createMockLevelLogger();
|
||||
|
||||
describe('Get CSV Job', () => {
|
||||
let mockJobParams: JobParamsPanelCsv;
|
||||
let mockSearchPanel: SearchPanel;
|
||||
let mockSavedObjectsClient: any;
|
||||
let mockUiSettingsClient: any;
|
||||
beforeEach(() => {
|
||||
mockJobParams = { savedObjectType: 'search', savedObjectId: '234-ididid' };
|
||||
mockSearchPanel = {
|
||||
indexPatternSavedObjectId: '123-indexId',
|
||||
attributes: {
|
||||
title: 'my search',
|
||||
sort: [],
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSource: { query: { isSearchSourceQuery: true }, filter: [] },
|
||||
},
|
||||
uiState: 56,
|
||||
},
|
||||
timerange: { timezone: 'PST', min: 0, max: 100 },
|
||||
};
|
||||
mockSavedObjectsClient = {
|
||||
get: () => ({
|
||||
attributes: { fields: null, title: null, timeFieldName: null },
|
||||
}),
|
||||
};
|
||||
mockUiSettingsClient = {
|
||||
get: () => ({}),
|
||||
};
|
||||
});
|
||||
|
||||
it('creates a data structure needed by generateCsv', async () => {
|
||||
const result = await getGenerateCsvParams(
|
||||
mockJobParams,
|
||||
mockSearchPanel,
|
||||
mockSavedObjectsClient,
|
||||
mockUiSettingsClient,
|
||||
logger
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"browserTimezone": "PST",
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [],
|
||||
"indexPatternSavedObject": Object {
|
||||
"attributes": Object {
|
||||
"fields": null,
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"fields": Array [],
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"metaFields": Array [],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [],
|
||||
},
|
||||
"docvalue_fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [],
|
||||
},
|
||||
"index": null,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('uses query and sort from the payload', async () => {
|
||||
mockJobParams.post = {
|
||||
state: {
|
||||
query: ['this is the query'],
|
||||
sort: ['this is the sort'],
|
||||
},
|
||||
};
|
||||
const result = await getGenerateCsvParams(
|
||||
mockJobParams,
|
||||
mockSearchPanel,
|
||||
mockSavedObjectsClient,
|
||||
mockUiSettingsClient,
|
||||
logger
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"browserTimezone": "PST",
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [],
|
||||
"indexPatternSavedObject": Object {
|
||||
"attributes": Object {
|
||||
"fields": null,
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"fields": Array [],
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"metaFields": Array [],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [],
|
||||
},
|
||||
"docvalue_fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"0": "this is the query",
|
||||
},
|
||||
],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [
|
||||
"this is the sort",
|
||||
],
|
||||
},
|
||||
"index": null,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('uses timerange timezone from the payload', async () => {
|
||||
mockJobParams.post = {
|
||||
timerange: { timezone: 'Africa/Timbuktu', min: 0, max: 9000 },
|
||||
};
|
||||
const result = await getGenerateCsvParams(
|
||||
mockJobParams,
|
||||
mockSearchPanel,
|
||||
mockSavedObjectsClient,
|
||||
mockUiSettingsClient,
|
||||
logger
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"browserTimezone": "Africa/Timbuktu",
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [],
|
||||
"indexPatternSavedObject": Object {
|
||||
"attributes": Object {
|
||||
"fields": null,
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"fields": Array [],
|
||||
"timeFieldName": null,
|
||||
"title": null,
|
||||
},
|
||||
"metaFields": Array [],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [],
|
||||
},
|
||||
"docvalue_fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [],
|
||||
},
|
||||
"index": null,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('uses timerange min and max (numeric) when index pattern has timefieldName', async () => {
|
||||
mockJobParams.post = {
|
||||
timerange: { timezone: 'Africa/Timbuktu', min: 0, max: 900000000 },
|
||||
};
|
||||
mockSavedObjectsClient = {
|
||||
get: () => ({
|
||||
attributes: { fields: null, title: 'test search', timeFieldName: '@test_time' },
|
||||
}),
|
||||
};
|
||||
const result = await getGenerateCsvParams(
|
||||
mockJobParams,
|
||||
mockSearchPanel,
|
||||
mockSavedObjectsClient,
|
||||
mockUiSettingsClient,
|
||||
logger
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"browserTimezone": "Africa/Timbuktu",
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [
|
||||
"@test_time",
|
||||
],
|
||||
"indexPatternSavedObject": Object {
|
||||
"attributes": Object {
|
||||
"fields": null,
|
||||
"timeFieldName": "@test_time",
|
||||
"title": "test search",
|
||||
},
|
||||
"fields": Array [],
|
||||
"timeFieldName": "@test_time",
|
||||
"title": "test search",
|
||||
},
|
||||
"metaFields": Array [],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [
|
||||
"@test_time",
|
||||
],
|
||||
},
|
||||
"docvalue_fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@test_time": Object {
|
||||
"format": "strict_date_time",
|
||||
"gte": "1970-01-01T00:00:00Z",
|
||||
"lte": "1970-01-11T10:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [],
|
||||
},
|
||||
"index": "test search",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('uses timerange min and max (string) when index pattern has timefieldName', async () => {
|
||||
mockJobParams.post = {
|
||||
timerange: {
|
||||
timezone: 'Africa/Timbuktu',
|
||||
min: '1980-01-01T00:00:00Z',
|
||||
max: '1990-01-01T00:00:00Z',
|
||||
},
|
||||
};
|
||||
mockSavedObjectsClient = {
|
||||
get: () => ({
|
||||
attributes: { fields: null, title: 'test search', timeFieldName: '@test_time' },
|
||||
}),
|
||||
};
|
||||
const result = await getGenerateCsvParams(
|
||||
mockJobParams,
|
||||
mockSearchPanel,
|
||||
mockSavedObjectsClient,
|
||||
mockUiSettingsClient,
|
||||
logger
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"browserTimezone": "Africa/Timbuktu",
|
||||
"conflictedTypesFields": Array [],
|
||||
"fields": Array [
|
||||
"@test_time",
|
||||
],
|
||||
"indexPatternSavedObject": Object {
|
||||
"attributes": Object {
|
||||
"fields": null,
|
||||
"timeFieldName": "@test_time",
|
||||
"title": "test search",
|
||||
},
|
||||
"fields": Array [],
|
||||
"timeFieldName": "@test_time",
|
||||
"title": "test search",
|
||||
},
|
||||
"metaFields": Array [],
|
||||
"searchRequest": Object {
|
||||
"body": Object {
|
||||
"_source": Object {
|
||||
"includes": Array [
|
||||
"@test_time",
|
||||
],
|
||||
},
|
||||
"docvalue_fields": undefined,
|
||||
"query": Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"@test_time": Object {
|
||||
"format": "strict_date_time",
|
||||
"gte": "1980-01-01T00:00:00Z",
|
||||
"lte": "1990-01-01T00:00:00Z",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"must": Array [],
|
||||
"must_not": Array [],
|
||||
"should": Array [],
|
||||
},
|
||||
},
|
||||
"script_fields": Object {},
|
||||
"sort": Array [],
|
||||
},
|
||||
"index": "test search",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IUiSettingsClient, SavedObjectsClientContract } from 'kibana/server';
|
||||
import { EsQueryConfig } from 'src/plugins/data/server';
|
||||
import { esQuery, Filter, Query } from '../../../../../../../src/plugins/data/server';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { TimeRangeParams } from '../../common';
|
||||
import { GenerateCsvParams } from '../../csv/generate_csv';
|
||||
import {
|
||||
DocValueFields,
|
||||
IndexPatternField,
|
||||
JobParamsPanelCsv,
|
||||
QueryFilter,
|
||||
SavedSearchObjectAttributes,
|
||||
SearchPanel,
|
||||
SearchSource,
|
||||
} from '../types';
|
||||
import { getDataSource } from './get_data_source';
|
||||
import { getFilters } from './get_filters';
|
||||
|
||||
export const getEsQueryConfig = async (config: IUiSettingsClient) => {
|
||||
const configs = await Promise.all([
|
||||
config.get('query:allowLeadingWildcards'),
|
||||
config.get('query:queryString:options'),
|
||||
config.get('courier:ignoreFilterIfFieldNotInIndex'),
|
||||
]);
|
||||
const [allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex] = configs;
|
||||
return {
|
||||
allowLeadingWildcards,
|
||||
queryStringOptions,
|
||||
ignoreFilterIfFieldNotInIndex,
|
||||
} as EsQueryConfig;
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a CSV Job object for CSV From SavedObject to use as a job parameter
|
||||
* for generateCsv
|
||||
*/
|
||||
export const getGenerateCsvParams = async (
|
||||
jobParams: JobParamsPanelCsv,
|
||||
panel: SearchPanel,
|
||||
savedObjectsClient: SavedObjectsClientContract,
|
||||
uiConfig: IUiSettingsClient,
|
||||
logger: LevelLogger
|
||||
): Promise<GenerateCsvParams> => {
|
||||
let timerange: TimeRangeParams | null;
|
||||
if (jobParams.post?.timerange) {
|
||||
timerange = jobParams.post?.timerange;
|
||||
} else {
|
||||
timerange = panel.timerange || null;
|
||||
}
|
||||
const { indexPatternSavedObjectId } = panel;
|
||||
const savedSearchObjectAttr = panel.attributes as SavedSearchObjectAttributes;
|
||||
const { indexPatternSavedObject } = await getDataSource(
|
||||
savedObjectsClient,
|
||||
indexPatternSavedObjectId
|
||||
);
|
||||
const esQueryConfig = await getEsQueryConfig(uiConfig);
|
||||
|
||||
const {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSource: {
|
||||
filter: [searchSourceFilter],
|
||||
query: searchSourceQuery,
|
||||
},
|
||||
},
|
||||
} = savedSearchObjectAttr as { kibanaSavedObjectMeta: { searchSource: SearchSource } };
|
||||
|
||||
const {
|
||||
timeFieldName: indexPatternTimeField,
|
||||
title: esIndex,
|
||||
fields: indexPatternFields,
|
||||
} = indexPatternSavedObject;
|
||||
|
||||
if (!indexPatternFields || indexPatternFields.length === 0) {
|
||||
logger.error(
|
||||
new Error(
|
||||
`No fields are selected in the saved search! Please select fields as columns in the saved search and try again.`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let payloadQuery: QueryFilter | undefined;
|
||||
let payloadSort: any[] = [];
|
||||
let docValueFields: DocValueFields[] | undefined;
|
||||
if (jobParams.post && jobParams.post.state) {
|
||||
({
|
||||
post: {
|
||||
state: { query: payloadQuery, sort: payloadSort = [], docvalue_fields: docValueFields },
|
||||
},
|
||||
} = jobParams);
|
||||
}
|
||||
const { includes, combinedFilter } = getFilters(
|
||||
indexPatternSavedObjectId,
|
||||
indexPatternTimeField,
|
||||
timerange,
|
||||
savedSearchObjectAttr,
|
||||
searchSourceFilter,
|
||||
payloadQuery
|
||||
);
|
||||
|
||||
const savedSortConfigs = savedSearchObjectAttr.sort;
|
||||
const sortConfig = [...payloadSort];
|
||||
savedSortConfigs.forEach(([savedSortField, savedSortOrder]) => {
|
||||
sortConfig.push({ [savedSortField]: { order: savedSortOrder } });
|
||||
});
|
||||
|
||||
const scriptFieldsConfig =
|
||||
indexPatternFields &&
|
||||
indexPatternFields
|
||||
.filter((f: IndexPatternField) => f.scripted)
|
||||
.reduce((accum: any, curr: IndexPatternField) => {
|
||||
return {
|
||||
...accum,
|
||||
[curr.name]: {
|
||||
script: {
|
||||
source: curr.script,
|
||||
lang: curr.lang,
|
||||
},
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
|
||||
const searchRequest = {
|
||||
index: esIndex,
|
||||
body: {
|
||||
_source: { includes },
|
||||
docvalue_fields: docValueFields,
|
||||
query: esQuery.buildEsQuery(
|
||||
// compromise made while factoring out IIndexPattern type
|
||||
// @ts-expect-error
|
||||
indexPatternSavedObject,
|
||||
(searchSourceQuery as unknown) as Query,
|
||||
(combinedFilter as unknown) as Filter,
|
||||
esQueryConfig
|
||||
),
|
||||
script_fields: scriptFieldsConfig,
|
||||
sort: sortConfig,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
browserTimezone: timerange?.timezone,
|
||||
indexPatternSavedObject,
|
||||
searchRequest,
|
||||
fields: includes,
|
||||
metaFields: [],
|
||||
conflictedTypesFields: [],
|
||||
};
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IndexPatternSavedObjectDeprecatedCSV } from '../../csv/types';
|
||||
import { SavedObjectReference, SavedSearchObjectAttributesJSON, SearchSource } from '../types';
|
||||
|
||||
export async function getDataSource(
|
||||
savedObjectsClient: any,
|
||||
indexPatternId?: string,
|
||||
savedSearchObjectId?: string
|
||||
): Promise<{
|
||||
indexPatternSavedObject: IndexPatternSavedObjectDeprecatedCSV;
|
||||
searchSource: SearchSource | null;
|
||||
}> {
|
||||
let indexPatternSavedObject: IndexPatternSavedObjectDeprecatedCSV;
|
||||
let searchSource: SearchSource | null = null;
|
||||
|
||||
if (savedSearchObjectId) {
|
||||
try {
|
||||
const { attributes, references } = (await savedObjectsClient.get(
|
||||
'search',
|
||||
savedSearchObjectId
|
||||
)) as { attributes: SavedSearchObjectAttributesJSON; references: SavedObjectReference[] };
|
||||
searchSource = JSON.parse(attributes.kibanaSavedObjectMeta.searchSourceJSON);
|
||||
const { id: indexPatternFromSearchId } = references.find(
|
||||
({ type }) => type === 'index-pattern'
|
||||
) as { id: string };
|
||||
({ indexPatternSavedObject } = await getDataSource(
|
||||
savedObjectsClient,
|
||||
indexPatternFromSearchId
|
||||
));
|
||||
return { searchSource, indexPatternSavedObject };
|
||||
} catch (err) {
|
||||
throw new Error(`Could not get saved search info! ${err}`);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const { attributes } = await savedObjectsClient.get('index-pattern', indexPatternId);
|
||||
const { fields, title, timeFieldName } = attributes;
|
||||
const parsedFields = fields ? JSON.parse(fields) : [];
|
||||
|
||||
indexPatternSavedObject = {
|
||||
fields: parsedFields,
|
||||
title,
|
||||
timeFieldName,
|
||||
attributes,
|
||||
};
|
||||
} catch (err) {
|
||||
throw new Error(`Could not get index pattern saved object! ${err}`);
|
||||
}
|
||||
return { indexPatternSavedObject, searchSource };
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { TimeRangeParams } from '../../common';
|
||||
import { QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types';
|
||||
import { getFilters } from './get_filters';
|
||||
|
||||
interface Args {
|
||||
indexPatternId: string;
|
||||
indexPatternTimeField: string | null;
|
||||
timerange: TimeRangeParams | null;
|
||||
savedSearchObjectAttr: SavedSearchObjectAttributes;
|
||||
searchSourceFilter: SearchSourceFilter;
|
||||
queryFilter: QueryFilter;
|
||||
}
|
||||
|
||||
describe('CSV from Saved Object: get_filters', () => {
|
||||
let args: Args;
|
||||
beforeEach(() => {
|
||||
args = {
|
||||
indexPatternId: 'logs-test-*',
|
||||
indexPatternTimeField: 'testtimestamp',
|
||||
timerange: {
|
||||
timezone: 'UTC',
|
||||
min: '1901-01-01T00:00:00.000Z',
|
||||
max: '1902-01-01T00:00:00.000Z',
|
||||
},
|
||||
savedSearchObjectAttr: {
|
||||
title: 'test',
|
||||
sort: [{ sortField: { order: 'asc' } }],
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSource: {
|
||||
query: { isSearchSourceQuery: true },
|
||||
filter: ['hello searchSource filter 1'],
|
||||
},
|
||||
},
|
||||
columns: ['larry'],
|
||||
uiState: null,
|
||||
},
|
||||
searchSourceFilter: { isSearchSourceFilter: true, isFilter: true },
|
||||
queryFilter: { isQueryFilter: true, isFilter: true },
|
||||
};
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('for timebased search', () => {
|
||||
const filters = getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr,
|
||||
args.searchSourceFilter,
|
||||
args.queryFilter
|
||||
);
|
||||
|
||||
expect(filters).toEqual({
|
||||
combinedFilter: [
|
||||
{
|
||||
range: {
|
||||
testtimestamp: {
|
||||
format: 'strict_date_time',
|
||||
gte: '1901-01-01T00:00:00Z',
|
||||
lte: '1902-01-01T00:00:00Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ isFilter: true, isSearchSourceFilter: true },
|
||||
{ isFilter: true, isQueryFilter: true },
|
||||
],
|
||||
includes: ['testtimestamp', 'larry'],
|
||||
timezone: 'UTC',
|
||||
});
|
||||
});
|
||||
|
||||
it('for non-timebased search', () => {
|
||||
args.indexPatternTimeField = null;
|
||||
args.timerange = null;
|
||||
|
||||
const filters = getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr,
|
||||
args.searchSourceFilter,
|
||||
args.queryFilter
|
||||
);
|
||||
|
||||
expect(filters).toEqual({
|
||||
combinedFilter: [
|
||||
{ isFilter: true, isSearchSourceFilter: true },
|
||||
{ isFilter: true, isQueryFilter: true },
|
||||
],
|
||||
includes: ['larry'],
|
||||
timezone: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('throw if timebased and timerange is missing', () => {
|
||||
args.timerange = null;
|
||||
|
||||
const throwFn = () =>
|
||||
getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr,
|
||||
args.searchSourceFilter,
|
||||
args.queryFilter
|
||||
);
|
||||
|
||||
expect(throwFn).toThrow(
|
||||
'Time range params are required for index pattern [logs-test-*], using time field [testtimestamp]'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('composes the defined filters', () => {
|
||||
expect(
|
||||
getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr,
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
).toEqual({
|
||||
combinedFilter: [
|
||||
{
|
||||
range: {
|
||||
testtimestamp: {
|
||||
format: 'strict_date_time',
|
||||
gte: '1901-01-01T00:00:00Z',
|
||||
lte: '1902-01-01T00:00:00Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
includes: ['testtimestamp', 'larry'],
|
||||
timezone: 'UTC',
|
||||
});
|
||||
|
||||
expect(
|
||||
getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr,
|
||||
undefined,
|
||||
args.queryFilter
|
||||
)
|
||||
).toEqual({
|
||||
combinedFilter: [
|
||||
{
|
||||
range: {
|
||||
testtimestamp: {
|
||||
format: 'strict_date_time',
|
||||
gte: '1901-01-01T00:00:00Z',
|
||||
lte: '1902-01-01T00:00:00Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ isFilter: true, isQueryFilter: true },
|
||||
],
|
||||
includes: ['testtimestamp', 'larry'],
|
||||
timezone: 'UTC',
|
||||
});
|
||||
});
|
||||
|
||||
describe('timefilter', () => {
|
||||
it('formats the datetime to the provided timezone', () => {
|
||||
args.timerange = {
|
||||
timezone: 'MST',
|
||||
min: '1901-01-01T00:00:00Z',
|
||||
max: '1902-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
expect(
|
||||
getFilters(
|
||||
args.indexPatternId,
|
||||
args.indexPatternTimeField,
|
||||
args.timerange,
|
||||
args.savedSearchObjectAttr
|
||||
)
|
||||
).toEqual({
|
||||
combinedFilter: [
|
||||
{
|
||||
range: {
|
||||
testtimestamp: {
|
||||
format: 'strict_date_time',
|
||||
gte: '1900-12-31T17:00:00-07:00',
|
||||
lte: '1901-12-31T17:00:00-07:00',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
includes: ['testtimestamp', 'larry'],
|
||||
timezone: 'MST',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { badRequest } from '@hapi/boom';
|
||||
import moment from 'moment-timezone';
|
||||
import { TimeRangeParams } from '../../common';
|
||||
import { Filter, QueryFilter, SavedSearchObjectAttributes, SearchSourceFilter } from '../types';
|
||||
|
||||
export function getFilters(
|
||||
indexPatternId: string,
|
||||
indexPatternTimeField: string | null,
|
||||
timerange: TimeRangeParams | null,
|
||||
savedSearchObjectAttr: SavedSearchObjectAttributes,
|
||||
searchSourceFilter?: SearchSourceFilter,
|
||||
queryFilter?: QueryFilter
|
||||
) {
|
||||
let includes: string[];
|
||||
let timeFilter: any | null;
|
||||
let timezone: string | null;
|
||||
|
||||
if (indexPatternTimeField) {
|
||||
if (!timerange || timerange.min == null || timerange.max == null) {
|
||||
throw badRequest(
|
||||
`Time range params are required for index pattern [${indexPatternId}], using time field [${indexPatternTimeField}]`
|
||||
);
|
||||
}
|
||||
|
||||
timezone = timerange.timezone;
|
||||
const { min: gte, max: lte } = timerange;
|
||||
timeFilter = {
|
||||
range: {
|
||||
[indexPatternTimeField]: {
|
||||
format: 'strict_date_time',
|
||||
gte: moment.tz(moment(gte), timezone).format(),
|
||||
lte: moment.tz(moment(lte), timezone).format(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const savedSearchCols = savedSearchObjectAttr.columns || [];
|
||||
includes = [indexPatternTimeField, ...savedSearchCols];
|
||||
} else {
|
||||
includes = savedSearchObjectAttr.columns || [];
|
||||
timeFilter = null;
|
||||
timezone = null;
|
||||
}
|
||||
|
||||
const combinedFilter: Filter[] = [timeFilter, searchSourceFilter, queryFilter].filter(Boolean); // builds an array of defined filters
|
||||
|
||||
return { timezone, combinedFilter, includes };
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { TimeRangeParams } from '../common';
|
||||
|
||||
export interface FakeRequest {
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface JobParamsPanelCsvPost {
|
||||
timerange?: TimeRangeParams;
|
||||
state?: any;
|
||||
}
|
||||
|
||||
export interface SearchPanel {
|
||||
indexPatternSavedObjectId: string;
|
||||
attributes: SavedSearchObjectAttributes;
|
||||
timerange?: TimeRangeParams;
|
||||
}
|
||||
|
||||
export interface JobPayloadPanelCsv extends JobParamsPanelCsv {
|
||||
panel: SearchPanel;
|
||||
}
|
||||
|
||||
export interface JobParamsPanelCsv {
|
||||
savedObjectType: string;
|
||||
savedObjectId: string;
|
||||
post?: JobParamsPanelCsvPost;
|
||||
visType?: string;
|
||||
}
|
||||
|
||||
export interface SavedObjectServiceError {
|
||||
statusCode: number;
|
||||
error?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface SavedObjectMetaJSON {
|
||||
searchSourceJSON: string;
|
||||
}
|
||||
|
||||
export interface SavedObjectMeta {
|
||||
searchSource: SearchSource;
|
||||
}
|
||||
|
||||
export interface SavedSearchObjectAttributesJSON {
|
||||
title: string;
|
||||
sort: any[];
|
||||
columns: string[];
|
||||
kibanaSavedObjectMeta: SavedObjectMetaJSON;
|
||||
uiState: any;
|
||||
}
|
||||
|
||||
export interface SavedSearchObjectAttributes {
|
||||
title: string;
|
||||
sort: any[];
|
||||
columns?: string[];
|
||||
kibanaSavedObjectMeta: SavedObjectMeta;
|
||||
uiState: any;
|
||||
}
|
||||
|
||||
export interface VisObjectAttributesJSON {
|
||||
title: string;
|
||||
visState: string; // JSON string
|
||||
type: string;
|
||||
params: any;
|
||||
uiStateJSON: string; // also JSON string
|
||||
aggs: any[];
|
||||
sort: any[];
|
||||
kibanaSavedObjectMeta: SavedObjectMeta;
|
||||
}
|
||||
|
||||
export interface VisObjectAttributes {
|
||||
title: string;
|
||||
visState: string; // JSON string
|
||||
type: string;
|
||||
params: any;
|
||||
uiState: {
|
||||
vis: {
|
||||
params: {
|
||||
sort: {
|
||||
columnIndex: string;
|
||||
direction: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
aggs: any[];
|
||||
sort: any[];
|
||||
kibanaSavedObjectMeta: SavedObjectMeta;
|
||||
}
|
||||
|
||||
export interface SavedObjectReference {
|
||||
name: string; // should be kibanaSavedObjectMeta.searchSourceJSON.index
|
||||
type: string; // should be index-pattern
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface SavedObject {
|
||||
attributes: any;
|
||||
references: SavedObjectReference[];
|
||||
}
|
||||
|
||||
export interface VisPanel {
|
||||
indexPatternSavedObjectId?: string;
|
||||
savedSearchObjectId?: string;
|
||||
attributes: VisObjectAttributes;
|
||||
timerange: TimeRangeParams;
|
||||
}
|
||||
|
||||
export interface DocValueFields {
|
||||
field: string;
|
||||
format: string;
|
||||
}
|
||||
|
||||
export interface SearchSourceQuery {
|
||||
isSearchSourceQuery: boolean;
|
||||
}
|
||||
|
||||
export interface SearchSource {
|
||||
query: SearchSourceQuery;
|
||||
filter: any[];
|
||||
}
|
||||
|
||||
/*
|
||||
* These filter types are stub types to help ensure things get passed to
|
||||
* non-Typescript functions in the right order. An actual structure is not
|
||||
* needed because the code doesn't look into the properties; just combines them
|
||||
* and passes them through to other non-TS modules.
|
||||
*/
|
||||
export interface Filter {
|
||||
isFilter: boolean;
|
||||
}
|
||||
export interface TimeFilter extends Filter {
|
||||
isTimeFilter: boolean;
|
||||
}
|
||||
export interface QueryFilter extends Filter {
|
||||
isQueryFilter: boolean;
|
||||
}
|
||||
export interface SearchSourceFilter extends Filter {
|
||||
isSearchSourceFilter: boolean;
|
||||
}
|
||||
|
||||
export interface IndexPatternField {
|
||||
scripted: boolean;
|
||||
lang?: string;
|
||||
script?: string;
|
||||
name: string;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CSV_JOB_TYPE } from '../../../common/constants';
|
||||
import { cryptoFactory } from '../../lib';
|
||||
import { CreateJobFn, CreateJobFnFactory } from '../../types';
|
||||
import { JobParamsCSV, TaskPayloadCSV } from './types';
|
||||
|
||||
export const createJobFnFactory: CreateJobFnFactory<
|
||||
CreateJobFn<JobParamsCSV, TaskPayloadCSV>
|
||||
> = function createJobFactoryFn(reporting, parentLogger) {
|
||||
const logger = parentLogger.clone([CSV_JOB_TYPE, 'create-job']);
|
||||
|
||||
const config = reporting.getConfig();
|
||||
const crypto = cryptoFactory(config.get('encryptionKey'));
|
||||
|
||||
return async function createJob(jobParams, context, request) {
|
||||
const serializedEncryptedHeaders = await crypto.encrypt(request.headers);
|
||||
|
||||
return {
|
||||
headers: serializedEncryptedHeaders,
|
||||
spaceId: reporting.getSpaceId(request, logger),
|
||||
...jobParams,
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('./generate_csv/generate_csv', () => ({
|
||||
CsvGenerator: class CsvGeneratorMock {
|
||||
generateData() {
|
||||
return {
|
||||
content: 'test\n123',
|
||||
};
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
import nodeCrypto from '@elastic/node-crypto';
|
||||
import { ReportingCore } from '../../';
|
||||
import { CancellationToken } from '../../../common';
|
||||
import {
|
||||
createMockConfig,
|
||||
createMockConfigSchema,
|
||||
createMockLevelLogger,
|
||||
createMockReportingCore,
|
||||
} from '../../test_helpers';
|
||||
import { runTaskFnFactory } from './execute_job';
|
||||
|
||||
const logger = createMockLevelLogger();
|
||||
const encryptionKey = 'tetkey';
|
||||
const headers = { sid: 'cooltestheaders' };
|
||||
let encryptedHeaders: string;
|
||||
let reportingCore: ReportingCore;
|
||||
|
||||
beforeAll(async () => {
|
||||
const crypto = nodeCrypto({ encryptionKey });
|
||||
const config = createMockConfig(
|
||||
createMockConfigSchema({
|
||||
encryptionKey,
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
escapeFormulaValues: true,
|
||||
maxSizeBytes: 180000,
|
||||
scroll: { size: 500, duration: '30s' },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
encryptedHeaders = await crypto.encrypt(headers);
|
||||
|
||||
reportingCore = await createMockReportingCore(config);
|
||||
});
|
||||
|
||||
test('gets the csv content from job parameters', async () => {
|
||||
const runTask = runTaskFnFactory(reportingCore, logger);
|
||||
|
||||
const payload = await runTask(
|
||||
'cool-job-id',
|
||||
{
|
||||
headers: encryptedHeaders,
|
||||
browserTimezone: 'US/Alaska',
|
||||
searchSource: {},
|
||||
objectType: 'search',
|
||||
title: 'Test Search',
|
||||
},
|
||||
new CancellationToken()
|
||||
);
|
||||
|
||||
expect(payload).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"content": "test
|
||||
123",
|
||||
}
|
||||
`);
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CSV_JOB_TYPE } from '../../../common/constants';
|
||||
import { getFieldFormats } from '../../services';
|
||||
import { RunTaskFn, RunTaskFnFactory } from '../../types';
|
||||
import { decryptJobHeaders } from '../common';
|
||||
import { CsvGenerator } from './generate_csv/generate_csv';
|
||||
import { TaskPayloadCSV } from './types';
|
||||
|
||||
export const runTaskFnFactory: RunTaskFnFactory<RunTaskFn<TaskPayloadCSV>> = (
|
||||
reporting,
|
||||
parentLogger
|
||||
) => {
|
||||
const config = reporting.getConfig();
|
||||
|
||||
return async function runTask(jobId, job, cancellationToken) {
|
||||
const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job', jobId]);
|
||||
|
||||
const encryptionKey = config.get('encryptionKey');
|
||||
const headers = await decryptJobHeaders(encryptionKey, job.headers, logger);
|
||||
const fakeRequest = reporting.getFakeRequest({ headers }, job.spaceId, logger);
|
||||
const uiSettings = await reporting.getUiSettingsClient(fakeRequest, logger);
|
||||
const dataPluginStart = await reporting.getDataService();
|
||||
const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings);
|
||||
|
||||
const [es, searchSourceStart] = await Promise.all([
|
||||
(await reporting.getEsClient()).asScoped(fakeRequest),
|
||||
await dataPluginStart.search.searchSource.asScoped(fakeRequest),
|
||||
]);
|
||||
|
||||
const clients = {
|
||||
uiSettings,
|
||||
data: dataPluginStart.search.asScoped(fakeRequest),
|
||||
es,
|
||||
};
|
||||
const dependencies = {
|
||||
searchSourceStart,
|
||||
fieldFormatsRegistry,
|
||||
};
|
||||
|
||||
const csv = new CsvGenerator(job, config, clients, dependencies, cancellationToken, logger);
|
||||
return await csv.generateData();
|
||||
};
|
||||
};
|
|
@ -0,0 +1,163 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`fields cells can be multi-value 1`] = `
|
||||
"\\"_id\\",sku
|
||||
\\"my-cool-id\\",\\"This is a cool SKU., This is also a cool SKU.\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`fields provides top-level underscored fields as columns 1`] = `
|
||||
"\\"_id\\",\\"_index\\",date,message
|
||||
\\"my-cool-id\\",\\"my-cool-index\\",\\"2020-12-31T00:14:28.000Z\\",\\"it's nice to see you\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`fields sorts the fields when they are to be used as table column names 1`] = `
|
||||
"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",date,\\"message_t\\",\\"message_u\\",\\"message_v\\",\\"message_w\\",\\"message_x\\",\\"message_y\\",\\"message_z\\"
|
||||
\\"my-cool-id\\",\\"my-cool-index\\",\\"'-\\",\\"'-\\",\\"2020-12-31T00:14:28.000Z\\",\\"test field T\\",\\"test field U\\",\\"test field V\\",\\"test field W\\",\\"test field X\\",\\"test field Y\\",\\"test field Z\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`formats a search result to CSV content 1`] = `
|
||||
"date,ip,message
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is a great message!\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`formats an empty search result to CSV content 1`] = `
|
||||
"date,ip,message
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`formulas can check for formulas, without escaping them 1`] = `
|
||||
"date,ip,message
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"=SUM(A1:A2)\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`formulas escapes formula values in a cell, doesn't warn the csv contains formulas 1`] = `
|
||||
"date,ip,message
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"'=SUM(A1:A2)\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`formulas escapes formula values in a header, doesn't warn the csv contains formulas 1`] = `
|
||||
"date,ip,\\"'=SUM(A1:A2)\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"This is great data\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`uses the scrollId to page all the data 1`] = `
|
||||
"date,ip,message
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from the initial search\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"hit from a subsequent scroll\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`warns if max size was reached 1`] = `
|
||||
"date,ip,message
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\"
|
||||
\\"2020-12-31T00:14:28.000Z\\",\\"110.135.176.89\\",\\"super cali fragile istic XPLA docious\\"
|
||||
"
|
||||
`;
|
|
@ -0,0 +1,645 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { identity, range } from 'lodash';
|
||||
import { IScopedClusterClient, IUiSettingsClient, SearchResponse } from 'src/core/server';
|
||||
import {
|
||||
elasticsearchServiceMock,
|
||||
savedObjectsClientMock,
|
||||
uiSettingsServiceMock,
|
||||
} from 'src/core/server/mocks';
|
||||
import { FieldFormatsRegistry, ISearchStartSearchSource } from 'src/plugins/data/common';
|
||||
import { searchSourceInstanceMock } from 'src/plugins/data/common/search/search_source/mocks';
|
||||
import { IScopedSearchClient } from 'src/plugins/data/server';
|
||||
import { dataPluginMock } from 'src/plugins/data/server/mocks';
|
||||
import { ReportingConfig } from '../../../';
|
||||
import { CancellationToken } from '../../../../common';
|
||||
import {
|
||||
UI_SETTINGS_CSV_QUOTE_VALUES,
|
||||
UI_SETTINGS_CSV_SEPARATOR,
|
||||
UI_SETTINGS_DATEFORMAT_TZ,
|
||||
} from '../../../../common/constants';
|
||||
import {
|
||||
createMockConfig,
|
||||
createMockConfigSchema,
|
||||
createMockLevelLogger,
|
||||
} from '../../../test_helpers';
|
||||
import { JobParamsCSV } from '../types';
|
||||
import { CsvGenerator } from './generate_csv';
|
||||
|
||||
const createMockJob = (baseObj: any = {}): JobParamsCSV => ({
|
||||
...baseObj,
|
||||
});
|
||||
|
||||
let mockEsClient: IScopedClusterClient;
|
||||
let mockDataClient: IScopedSearchClient;
|
||||
let mockConfig: ReportingConfig;
|
||||
let uiSettingsClient: IUiSettingsClient;
|
||||
|
||||
const searchSourceMock = { ...searchSourceInstanceMock };
|
||||
const mockSearchSourceService: jest.Mocked<ISearchStartSearchSource> = {
|
||||
create: jest.fn().mockReturnValue(searchSourceMock),
|
||||
createEmpty: jest.fn().mockReturnValue(searchSourceMock),
|
||||
};
|
||||
const mockDataClientSearchDefault = jest.fn().mockImplementation(
|
||||
(): Rx.Observable<{ rawResponse: SearchResponse<unknown> }> =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
took: 1,
|
||||
timed_out: false,
|
||||
_shards: { total: 1, successful: 1, failed: 0, skipped: 0 },
|
||||
hits: {
|
||||
hits: [],
|
||||
total: 0,
|
||||
max_score: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
const mockSearchSourceGetFieldDefault = jest.fn().mockImplementation((key: string) => {
|
||||
switch (key) {
|
||||
case 'fields':
|
||||
return ['date', 'ip', 'message'];
|
||||
case 'index':
|
||||
return {
|
||||
fields: {
|
||||
getByName: jest.fn().mockImplementation(() => []),
|
||||
getByType: jest.fn().mockImplementation(() => []),
|
||||
},
|
||||
getFormatterForField: jest.fn(),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const mockFieldFormatsRegistry = ({
|
||||
deserialize: jest
|
||||
.fn()
|
||||
.mockImplementation(() => ({ id: 'string', convert: jest.fn().mockImplementation(identity) })),
|
||||
} as unknown) as FieldFormatsRegistry;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockEsClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
mockDataClient = dataPluginMock.createStartContract().search.asScoped({} as any);
|
||||
mockDataClient.search = mockDataClientSearchDefault;
|
||||
|
||||
uiSettingsClient = uiSettingsServiceMock
|
||||
.createStartContract()
|
||||
.asScopedToClient(savedObjectsClientMock.create());
|
||||
uiSettingsClient.get = jest.fn().mockImplementation((key): any => {
|
||||
switch (key) {
|
||||
case UI_SETTINGS_CSV_QUOTE_VALUES:
|
||||
return true;
|
||||
case UI_SETTINGS_CSV_SEPARATOR:
|
||||
return ',';
|
||||
case UI_SETTINGS_DATEFORMAT_TZ:
|
||||
return 'Browser';
|
||||
}
|
||||
});
|
||||
|
||||
mockConfig = createMockConfig(
|
||||
createMockConfigSchema({
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
escapeFormulaValues: true,
|
||||
maxSizeBytes: 180000,
|
||||
scroll: { size: 500, duration: '30s' },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
searchSourceMock.getField = mockSearchSourceGetFieldDefault;
|
||||
});
|
||||
|
||||
const logger = createMockLevelLogger();
|
||||
|
||||
it('formats an empty search result to CSV content', async () => {
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
|
||||
it('formats a search result to CSV content', async () => {
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
fields: {
|
||||
date: `["2020-12-31T00:14:28.000Z"]`,
|
||||
ip: `["110.135.176.89"]`,
|
||||
message: `["This is a great message!"]`,
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
|
||||
const HITS_TOTAL = 100;
|
||||
|
||||
it('calculates the bytes of the content', async () => {
|
||||
searchSourceMock.getField = jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'fields') {
|
||||
return ['message'];
|
||||
}
|
||||
return mockSearchSourceGetFieldDefault(key);
|
||||
});
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: range(0, HITS_TOTAL).map((hit, i) => ({
|
||||
fields: {
|
||||
message: ['this is a great message'],
|
||||
},
|
||||
})),
|
||||
total: HITS_TOTAL,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
expect(csvResult.size).toBe(2608);
|
||||
expect(csvResult.max_size_reached).toBe(false);
|
||||
expect(csvResult.warnings).toEqual([]);
|
||||
});
|
||||
|
||||
it('warns if max size was reached', async () => {
|
||||
const TEST_MAX_SIZE = 500;
|
||||
|
||||
mockConfig = createMockConfig(
|
||||
createMockConfigSchema({
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
escapeFormulaValues: true,
|
||||
maxSizeBytes: TEST_MAX_SIZE,
|
||||
scroll: { size: 500, duration: '30s' },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: range(0, HITS_TOTAL).map((hit, i) => ({
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
message: ['super cali fragile istic XPLA docious'],
|
||||
},
|
||||
})),
|
||||
total: HITS_TOTAL,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
expect(csvResult.max_size_reached).toBe(true);
|
||||
expect(csvResult.warnings).toEqual([]);
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('uses the scrollId to page all the data', async () => {
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
_scroll_id: 'awesome-scroll-hero',
|
||||
hits: {
|
||||
hits: range(0, HITS_TOTAL / 10).map((hit, i) => ({
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
message: ['hit from the initial search'],
|
||||
},
|
||||
})),
|
||||
total: HITS_TOTAL,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
mockEsClient.asCurrentUser.scroll = jest.fn().mockResolvedValue({
|
||||
body: {
|
||||
hits: {
|
||||
hits: range(0, HITS_TOTAL / 10).map((hit, i) => ({
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
message: ['hit from a subsequent scroll'],
|
||||
},
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
expect(csvResult.warnings).toEqual([]);
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('fields', () => {
|
||||
it('cells can be multi-value', async () => {
|
||||
searchSourceMock.getField = jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'fields') {
|
||||
return ['_id', 'sku'];
|
||||
}
|
||||
return mockSearchSourceGetFieldDefault(key);
|
||||
});
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'my-cool-id',
|
||||
_index: 'my-cool-index',
|
||||
_version: 4,
|
||||
fields: {
|
||||
sku: [`This is a cool SKU.`, `This is also a cool SKU.`],
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({ searchSource: {} }),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('provides top-level underscored fields as columns', async () => {
|
||||
searchSourceMock.getField = jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'fields') {
|
||||
return ['_id', '_index', 'date', 'message'];
|
||||
}
|
||||
return mockSearchSourceGetFieldDefault(key);
|
||||
});
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'my-cool-id',
|
||||
_index: 'my-cool-index',
|
||||
_version: 4,
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
message: [`it's nice to see you`],
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
sort: [{ '@date': 'desc' }],
|
||||
index: '93f4bc50-6662-11eb-98bc-f550e2308366',
|
||||
fields: ['_id', '_index', '@date', 'message'],
|
||||
filter: [],
|
||||
},
|
||||
}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
|
||||
it('sorts the fields when they are to be used as table column names', async () => {
|
||||
searchSourceMock.getField = jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'fields') {
|
||||
return ['*'];
|
||||
}
|
||||
return mockSearchSourceGetFieldDefault(key);
|
||||
});
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'my-cool-id',
|
||||
_index: 'my-cool-index',
|
||||
_version: 4,
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
message_z: [`test field Z`],
|
||||
message_y: [`test field Y`],
|
||||
message_x: [`test field X`],
|
||||
message_w: [`test field W`],
|
||||
message_v: [`test field V`],
|
||||
message_u: [`test field U`],
|
||||
message_t: [`test field T`],
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
sort: [{ '@date': 'desc' }],
|
||||
index: '93f4bc50-6662-11eb-98bc-f550e2308366',
|
||||
fields: ['*'],
|
||||
filter: [],
|
||||
},
|
||||
}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formulas', () => {
|
||||
const TEST_FORMULA = '=SUM(A1:A2)';
|
||||
|
||||
it(`escapes formula values in a cell, doesn't warn the csv contains formulas`, async () => {
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
message: [TEST_FORMULA],
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
|
||||
it(`escapes formula values in a header, doesn't warn the csv contains formulas`, async () => {
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
[TEST_FORMULA]: 'This is great data',
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
searchSourceMock.getField = jest.fn().mockImplementation((key: string) => {
|
||||
if (key === 'fields') {
|
||||
return ['date', 'ip', TEST_FORMULA];
|
||||
}
|
||||
return mockSearchSourceGetFieldDefault(key);
|
||||
});
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(false);
|
||||
});
|
||||
|
||||
it('can check for formulas, without escaping them', async () => {
|
||||
mockConfig = createMockConfig(
|
||||
createMockConfigSchema({
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
escapeFormulaValues: false,
|
||||
maxSizeBytes: 180000,
|
||||
scroll: { size: 500, duration: '30s' },
|
||||
},
|
||||
})
|
||||
);
|
||||
mockDataClient.search = jest.fn().mockImplementation(() =>
|
||||
Rx.of({
|
||||
rawResponse: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
fields: {
|
||||
date: ['2020-12-31T00:14:28.000Z'],
|
||||
ip: ['110.135.176.89'],
|
||||
message: [TEST_FORMULA],
|
||||
},
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const generateCsv = new CsvGenerator(
|
||||
createMockJob({}),
|
||||
mockConfig,
|
||||
{
|
||||
es: mockEsClient,
|
||||
data: mockDataClient,
|
||||
uiSettings: uiSettingsClient,
|
||||
},
|
||||
{
|
||||
searchSourceStart: mockSearchSourceService,
|
||||
fieldFormatsRegistry: mockFieldFormatsRegistry,
|
||||
},
|
||||
new CancellationToken(),
|
||||
logger
|
||||
);
|
||||
|
||||
const csvResult = await generateCsv.generateData();
|
||||
|
||||
expect(csvResult.content).toMatchSnapshot();
|
||||
expect(csvResult.csv_contains_formulas).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { IScopedClusterClient, IUiSettingsClient } from 'src/core/server';
|
||||
import { IScopedSearchClient } from 'src/plugins/data/server';
|
||||
import { Datatable } from 'src/plugins/expressions/server';
|
||||
import { ReportingConfig } from '../../..';
|
||||
import {
|
||||
ES_SEARCH_STRATEGY,
|
||||
FieldFormat,
|
||||
FieldFormatConfig,
|
||||
IFieldFormatsRegistry,
|
||||
IndexPattern,
|
||||
ISearchSource,
|
||||
ISearchStartSearchSource,
|
||||
SearchFieldValue,
|
||||
tabifyDocs,
|
||||
} from '../../../../../../../src/plugins/data/common';
|
||||
import { KbnServerError } from '../../../../../../../src/plugins/kibana_utils/server';
|
||||
import { CancellationToken } from '../../../../common';
|
||||
import { CONTENT_TYPE_CSV } from '../../../../common/constants';
|
||||
import { byteSizeValueToNumber } from '../../../../common/schema_utils';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { TaskRunResult } from '../../../lib/tasks';
|
||||
import { JobParamsCSV } from '../types';
|
||||
import { cellHasFormulas } from './cell_has_formula';
|
||||
import { CsvExportSettings, getExportSettings } from './get_export_settings';
|
||||
import { MaxSizeStringBuilder } from './max_size_string_builder';
|
||||
|
||||
interface Clients {
|
||||
es: IScopedClusterClient;
|
||||
data: IScopedSearchClient;
|
||||
uiSettings: IUiSettingsClient;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
searchSourceStart: ISearchStartSearchSource;
|
||||
fieldFormatsRegistry: IFieldFormatsRegistry;
|
||||
}
|
||||
|
||||
// Function to check if the field name values can be used as the header row
|
||||
function isPlainStringArray(
|
||||
fields: SearchFieldValue[] | string | boolean | undefined
|
||||
): fields is string[] {
|
||||
let result = true;
|
||||
if (Array.isArray(fields)) {
|
||||
fields.forEach((field) => {
|
||||
if (typeof field !== 'string' || field === '*' || field === '_source') {
|
||||
result = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class CsvGenerator {
|
||||
private _formatters: Record<string, FieldFormat> | null = null;
|
||||
private csvContainsFormulas = false;
|
||||
private maxSizeReached = false;
|
||||
private csvRowCount = 0;
|
||||
|
||||
constructor(
|
||||
private job: JobParamsCSV,
|
||||
private config: ReportingConfig,
|
||||
private clients: Clients,
|
||||
private dependencies: Dependencies,
|
||||
private cancellationToken: CancellationToken,
|
||||
private logger: LevelLogger
|
||||
) {}
|
||||
|
||||
private async scan(
|
||||
index: IndexPattern,
|
||||
searchSource: ISearchSource,
|
||||
scrollSettings: CsvExportSettings['scroll']
|
||||
) {
|
||||
const searchBody = await searchSource.getSearchRequestBody();
|
||||
this.logger.debug(`executing search request`);
|
||||
const searchParams = {
|
||||
params: {
|
||||
body: searchBody,
|
||||
index: index.title,
|
||||
scroll: scrollSettings.duration,
|
||||
size: scrollSettings.size,
|
||||
},
|
||||
};
|
||||
const results = (
|
||||
await this.clients.data.search(searchParams, { strategy: ES_SEARCH_STRATEGY }).toPromise()
|
||||
).rawResponse;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async scroll(scrollId: string, scrollSettings: CsvExportSettings['scroll']) {
|
||||
this.logger.debug(`executing scroll request`);
|
||||
const results = (
|
||||
await this.clients.es.asCurrentUser.scroll({
|
||||
scroll: scrollSettings.duration,
|
||||
scroll_id: scrollId,
|
||||
})
|
||||
).body as SearchResponse<unknown>;
|
||||
return results;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load field formats for each field in the list
|
||||
*/
|
||||
private getFormatters(table: Datatable) {
|
||||
if (this._formatters) {
|
||||
return this._formatters;
|
||||
}
|
||||
|
||||
// initialize field formats
|
||||
const formatters: Record<string, FieldFormat> = {};
|
||||
table.columns.forEach((c) => {
|
||||
const fieldFormat = this.dependencies.fieldFormatsRegistry.deserialize(c.meta.params);
|
||||
formatters[c.id] = fieldFormat;
|
||||
});
|
||||
|
||||
this._formatters = formatters;
|
||||
return this._formatters;
|
||||
}
|
||||
|
||||
private escapeValues(settings: CsvExportSettings) {
|
||||
return (value: string) => {
|
||||
if (settings.checkForFormulas && cellHasFormulas(value)) {
|
||||
this.csvContainsFormulas = true; // set warning if cell value has a formula
|
||||
}
|
||||
return settings.escapeValue(value);
|
||||
};
|
||||
}
|
||||
|
||||
// use fields/fieldsFromSource from the searchSource to get the ordering of columns
|
||||
// otherwise use the table columns as they are
|
||||
private getFields(searchSource: ISearchSource, table: Datatable): string[] {
|
||||
const fieldValues: Record<string, string | boolean | SearchFieldValue[] | undefined> = {
|
||||
fields: searchSource.getField('fields'),
|
||||
fieldsFromSource: searchSource.getField('fieldsFromSource'),
|
||||
};
|
||||
const fieldSource = fieldValues.fieldsFromSource ? 'fieldsFromSource' : 'fields';
|
||||
this.logger.debug(`Getting search source fields from: '${fieldSource}'`);
|
||||
|
||||
const fields = fieldValues[fieldSource];
|
||||
// Check if field name values are string[] and if the fields are user-defined
|
||||
if (isPlainStringArray(fields)) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
// Default to using the table column IDs as the fields
|
||||
const columnIds = table.columns.map((c) => c.id);
|
||||
// Fields in the API response don't come sorted - they need to be sorted client-side
|
||||
columnIds.sort();
|
||||
return columnIds;
|
||||
}
|
||||
|
||||
private formatCellValues(formatters: Record<string, FieldFormat>) {
|
||||
return ({
|
||||
column: tableColumn,
|
||||
data: dataTableCell,
|
||||
}: {
|
||||
column: string;
|
||||
data: any;
|
||||
}): string => {
|
||||
let cell: string[] | string | object;
|
||||
// check truthiness to guard against _score, _type, etc
|
||||
if (tableColumn && dataTableCell) {
|
||||
try {
|
||||
cell = formatters[tableColumn].convert(dataTableCell);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
cell = '-';
|
||||
}
|
||||
|
||||
try {
|
||||
// expected values are a string of JSON where the value(s) is in an array
|
||||
cell = JSON.parse(cell);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// We have to strip singular array values out of their array wrapper,
|
||||
// So that the value appears the visually the same as seen in Discover
|
||||
if (Array.isArray(cell)) {
|
||||
cell = cell.map((c) => (typeof c === 'object' ? JSON.stringify(c) : c)).join(', ');
|
||||
}
|
||||
|
||||
// Check for object-type value (geoip)
|
||||
if (typeof cell === 'object') {
|
||||
cell = JSON.stringify(cell);
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
return '-'; // Unknown field: it existed in searchSource but has no value in the result
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Use the list of fields to generate the header row
|
||||
*/
|
||||
private generateHeader(
|
||||
fields: string[],
|
||||
table: Datatable,
|
||||
builder: MaxSizeStringBuilder,
|
||||
settings: CsvExportSettings
|
||||
) {
|
||||
this.logger.debug(`Building CSV header row...`);
|
||||
const header = fields.map(this.escapeValues(settings)).join(settings.separator) + '\n';
|
||||
|
||||
if (!builder.tryAppend(header)) {
|
||||
return {
|
||||
size: 0,
|
||||
content: '',
|
||||
maxSizeReached: true,
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Format a Datatable into rows of CSV content
|
||||
*/
|
||||
private generateRows(
|
||||
fields: string[],
|
||||
table: Datatable,
|
||||
builder: MaxSizeStringBuilder,
|
||||
formatters: Record<string, FieldFormat>,
|
||||
settings: CsvExportSettings
|
||||
) {
|
||||
this.logger.debug(`Building ${table.rows.length} CSV data rows...`);
|
||||
for (const dataTableRow of table.rows) {
|
||||
if (this.cancellationToken.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const row =
|
||||
fields
|
||||
.map((f) => ({ column: f, data: dataTableRow[f] }))
|
||||
.map(this.formatCellValues(formatters))
|
||||
.map(this.escapeValues(settings))
|
||||
.join(settings.separator) + '\n';
|
||||
|
||||
if (!builder.tryAppend(row)) {
|
||||
this.logger.warn(`Max Size Reached after ${this.csvRowCount} rows.`);
|
||||
this.maxSizeReached = true;
|
||||
if (this.cancellationToken) {
|
||||
this.cancellationToken.cancel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.csvRowCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public async generateData(): Promise<TaskRunResult> {
|
||||
const [settings, searchSource] = await Promise.all([
|
||||
getExportSettings(
|
||||
this.clients.uiSettings,
|
||||
this.config,
|
||||
this.job.browserTimezone,
|
||||
this.logger
|
||||
),
|
||||
this.dependencies.searchSourceStart.create(this.job.searchSource),
|
||||
]);
|
||||
|
||||
const index = searchSource.getField('index');
|
||||
|
||||
if (!index) {
|
||||
throw new Error(`The search must have a revference to an index pattern!`);
|
||||
}
|
||||
|
||||
const { maxSizeBytes, bom, escapeFormulaValues, scroll: scrollSettings } = settings;
|
||||
|
||||
const builder = new MaxSizeStringBuilder(byteSizeValueToNumber(maxSizeBytes), bom);
|
||||
const warnings: string[] = [];
|
||||
let first = true;
|
||||
let currentRecord = -1;
|
||||
let totalRecords = 0;
|
||||
let scrollId: string | undefined;
|
||||
|
||||
// apply timezone from the job to all date field formatters
|
||||
try {
|
||||
index.fields.getByType('date').forEach(({ name }) => {
|
||||
this.logger.debug(`setting timezone on ${name}`);
|
||||
const format: FieldFormatConfig = {
|
||||
...index.fieldFormatMap[name],
|
||||
id: index.fieldFormatMap[name]?.id || 'date', // allow id: date_nanos
|
||||
params: {
|
||||
...index.fieldFormatMap[name]?.params,
|
||||
timezone: settings.timezone,
|
||||
},
|
||||
};
|
||||
index.setFieldFormat(name, format);
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
do {
|
||||
if (this.cancellationToken.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
let results: SearchResponse<unknown> | undefined;
|
||||
if (scrollId == null) {
|
||||
// open a scroll cursor in Elasticsearch
|
||||
results = await this.scan(index, searchSource, scrollSettings);
|
||||
scrollId = results?._scroll_id;
|
||||
if (results.hits?.total != null) {
|
||||
totalRecords = results.hits.total;
|
||||
this.logger.debug(`Total search results: ${totalRecords}`);
|
||||
}
|
||||
} else {
|
||||
// use the scroll cursor in Elasticsearch
|
||||
results = await this.scroll(scrollId, scrollSettings);
|
||||
}
|
||||
|
||||
if (!results) {
|
||||
this.logger.warning(`Search results are undefined!`);
|
||||
break;
|
||||
}
|
||||
|
||||
let table: Datatable | undefined;
|
||||
try {
|
||||
table = tabifyDocs(results, index, { shallow: true, meta: true });
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
|
||||
if (!table) {
|
||||
break;
|
||||
}
|
||||
|
||||
const fields = this.getFields(searchSource, table);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
this.generateHeader(fields, table, builder, settings);
|
||||
}
|
||||
|
||||
if (table.rows.length < 1) {
|
||||
break; // empty report with just the header
|
||||
}
|
||||
|
||||
const formatters = this.getFormatters(table);
|
||||
this.generateRows(fields, table, builder, formatters, settings);
|
||||
|
||||
// update iterator
|
||||
currentRecord += table.rows.length;
|
||||
} while (currentRecord < totalRecords - 1);
|
||||
|
||||
// Add warnings to be logged
|
||||
if (this.csvContainsFormulas && escapeFormulaValues) {
|
||||
warnings.push(
|
||||
i18n.translate('xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues', {
|
||||
defaultMessage: 'CSV may contain formulas whose values have been escaped',
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
if (err instanceof KbnServerError && err.errBody) {
|
||||
throw JSON.stringify(err.errBody.error);
|
||||
}
|
||||
} finally {
|
||||
// clear scrollID
|
||||
if (scrollId) {
|
||||
this.logger.debug(`executing clearScroll request`);
|
||||
try {
|
||||
await this.clients.es.asCurrentUser.clearScroll({ scroll_id: [scrollId] });
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
} else {
|
||||
this.logger.warn(`No scrollId to clear!`);
|
||||
}
|
||||
}
|
||||
|
||||
const size = builder.getSizeInBytes();
|
||||
this.logger.debug(
|
||||
`Finished generating. Total size in bytes: ${size}. Row count: ${this.csvRowCount}.`
|
||||
);
|
||||
|
||||
return {
|
||||
content: builder.getString(),
|
||||
content_type: CONTENT_TYPE_CSV,
|
||||
csv_contains_formulas: this.csvContainsFormulas && !escapeFormulaValues,
|
||||
max_size_reached: this.maxSizeReached,
|
||||
size,
|
||||
warnings,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
UI_SETTINGS_DATEFORMAT_TZ,
|
||||
UI_SETTINGS_CSV_QUOTE_VALUES,
|
||||
UI_SETTINGS_CSV_SEPARATOR,
|
||||
} from '../../../../common/constants';
|
||||
import { IUiSettingsClient } from 'kibana/server';
|
||||
import { savedObjectsClientMock, uiSettingsServiceMock } from 'src/core/server/mocks';
|
||||
import {
|
||||
createMockConfig,
|
||||
createMockConfigSchema,
|
||||
createMockLevelLogger,
|
||||
} from '../../../test_helpers';
|
||||
import { getExportSettings } from './get_export_settings';
|
||||
|
||||
describe('getExportSettings', () => {
|
||||
let uiSettingsClient: IUiSettingsClient;
|
||||
const config = createMockConfig(createMockConfigSchema({}));
|
||||
const logger = createMockLevelLogger();
|
||||
|
||||
beforeEach(() => {
|
||||
uiSettingsClient = uiSettingsServiceMock
|
||||
.createStartContract()
|
||||
.asScopedToClient(savedObjectsClientMock.create());
|
||||
uiSettingsClient.get = jest.fn().mockImplementation((key: string) => {
|
||||
switch (key) {
|
||||
case UI_SETTINGS_CSV_QUOTE_VALUES:
|
||||
return true;
|
||||
case UI_SETTINGS_CSV_SEPARATOR:
|
||||
return ',';
|
||||
case UI_SETTINGS_DATEFORMAT_TZ:
|
||||
return 'Browser';
|
||||
}
|
||||
|
||||
return 'helo world';
|
||||
});
|
||||
});
|
||||
|
||||
test('getExportSettings: returns the expected result', async () => {
|
||||
expect(await getExportSettings(uiSettingsClient, config, '', logger)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"bom": "",
|
||||
"checkForFormulas": undefined,
|
||||
"escapeFormulaValues": undefined,
|
||||
"escapeValue": [Function],
|
||||
"maxSizeBytes": undefined,
|
||||
"scroll": Object {
|
||||
"duration": undefined,
|
||||
"size": undefined,
|
||||
},
|
||||
"separator": ",",
|
||||
"timezone": "UTC",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('escapeValue function', async () => {
|
||||
const { escapeValue } = await getExportSettings(uiSettingsClient, config, '', logger);
|
||||
expect(escapeValue(`test`)).toBe(`test`);
|
||||
expect(escapeValue(`this is, a test`)).toBe(`"this is, a test"`);
|
||||
expect(escapeValue(`"tet"`)).toBe(`"""tet"""`);
|
||||
expect(escapeValue(`@foo`)).toBe(`"@foo"`);
|
||||
});
|
||||
|
||||
test('non-default timezone', async () => {
|
||||
uiSettingsClient.get = jest.fn().mockImplementation((key: string) => {
|
||||
switch (key) {
|
||||
case UI_SETTINGS_DATEFORMAT_TZ:
|
||||
return `America/Aruba`;
|
||||
}
|
||||
});
|
||||
|
||||
expect(
|
||||
await getExportSettings(uiSettingsClient, config, '', logger).then(({ timezone }) => timezone)
|
||||
).toBe(`America/Aruba`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { IUiSettingsClient } from 'kibana/server';
|
||||
import { ReportingConfig } from '../../../';
|
||||
import {
|
||||
CSV_BOM_CHARS,
|
||||
UI_SETTINGS_DATEFORMAT_TZ,
|
||||
UI_SETTINGS_CSV_QUOTE_VALUES,
|
||||
UI_SETTINGS_CSV_SEPARATOR,
|
||||
} from '../../../../common/constants';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { createEscapeValue } from './escape_value';
|
||||
|
||||
export interface CsvExportSettings {
|
||||
timezone: string;
|
||||
scroll: {
|
||||
size: number;
|
||||
duration: string;
|
||||
};
|
||||
bom: string;
|
||||
separator: string;
|
||||
maxSizeBytes: number | ByteSizeValue;
|
||||
checkForFormulas: boolean;
|
||||
escapeFormulaValues: boolean;
|
||||
escapeValue: (value: string) => string;
|
||||
}
|
||||
|
||||
export const getExportSettings = async (
|
||||
client: IUiSettingsClient,
|
||||
config: ReportingConfig,
|
||||
timezone: string | undefined,
|
||||
logger: LevelLogger
|
||||
): Promise<CsvExportSettings> => {
|
||||
// Timezone
|
||||
let setTimezone: string;
|
||||
// timezone in job params?
|
||||
if (timezone) {
|
||||
setTimezone = timezone;
|
||||
} else {
|
||||
// timezone in settings?
|
||||
setTimezone = await client.get(UI_SETTINGS_DATEFORMAT_TZ);
|
||||
if (setTimezone === 'Browser') {
|
||||
// if `Browser`, hardcode it to 'UTC' so the export has data that makes sense
|
||||
logger.warn(
|
||||
i18n.translate('xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting', {
|
||||
defaultMessage:
|
||||
'Kibana Advanced Setting "{dateFormatTimezone}" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.',
|
||||
values: { dateFormatTimezone: 'dateFormat:tz' },
|
||||
})
|
||||
);
|
||||
setTimezone = 'UTC';
|
||||
}
|
||||
}
|
||||
|
||||
// Separator, QuoteValues
|
||||
const [separator, quoteValues] = await Promise.all([
|
||||
client.get(UI_SETTINGS_CSV_SEPARATOR),
|
||||
client.get(UI_SETTINGS_CSV_QUOTE_VALUES),
|
||||
]);
|
||||
|
||||
const escapeFormulaValues = config.get('csv', 'escapeFormulaValues');
|
||||
const escapeValue = createEscapeValue(quoteValues, escapeFormulaValues);
|
||||
const bom = config.get('csv', 'useByteOrderMarkEncoding') ? CSV_BOM_CHARS : '';
|
||||
|
||||
return {
|
||||
timezone: setTimezone,
|
||||
scroll: {
|
||||
size: config.get('csv', 'scroll', 'size'),
|
||||
duration: config.get('csv', 'scroll', 'duration'),
|
||||
},
|
||||
bom,
|
||||
separator,
|
||||
maxSizeBytes: config.get('csv', 'maxSizeBytes'),
|
||||
checkForFormulas: config.get('csv', 'checkForFormulas'),
|
||||
escapeFormulaValues,
|
||||
escapeValue,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { CsvGenerator } from './generate_csv';
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
CSV_JOB_TYPE as jobType,
|
||||
LICENSE_TYPE_BASIC,
|
||||
LICENSE_TYPE_ENTERPRISE,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_TRIAL,
|
||||
} from '../../../common/constants';
|
||||
import { CreateJobFn, ExportTypeDefinition, RunTaskFn } from '../../types';
|
||||
import { createJobFnFactory } from './create_job';
|
||||
import { runTaskFnFactory } from './execute_job';
|
||||
import { metadata } from './metadata';
|
||||
import { JobParamsCSV, TaskPayloadCSV } from './types';
|
||||
|
||||
export const getExportType = (): ExportTypeDefinition<
|
||||
CreateJobFn<JobParamsCSV>,
|
||||
RunTaskFn<TaskPayloadCSV>
|
||||
> => ({
|
||||
...metadata,
|
||||
jobType,
|
||||
jobContentExtension: 'csv',
|
||||
createJobFnFactory,
|
||||
runTaskFnFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
||||
LICENSE_TYPE_BASIC,
|
||||
LICENSE_TYPE_STANDARD,
|
||||
LICENSE_TYPE_GOLD,
|
||||
LICENSE_TYPE_PLATINUM,
|
||||
LICENSE_TYPE_ENTERPRISE,
|
||||
],
|
||||
});
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants';
|
||||
import { CSV_JOB_TYPE } from '../../../common/constants';
|
||||
|
||||
export const metadata = {
|
||||
id: CSV_FROM_SAVEDOBJECT_JOB_TYPE,
|
||||
name: CSV_FROM_SAVEDOBJECT_JOB_TYPE,
|
||||
id: 'csv_searchsource',
|
||||
name: CSV_JOB_TYPE,
|
||||
};
|
18
x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts
vendored
Normal file
18
x-pack/plugins/reporting/server/export_types/csv_searchsource/types.d.ts
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { BaseParams, BasePayload } from '../../types';
|
||||
|
||||
export type RawValue = string | object | null | undefined;
|
||||
|
||||
interface BaseParamsCSV {
|
||||
browserTimezone: string;
|
||||
searchSource: any;
|
||||
}
|
||||
|
||||
export type JobParamsCSV = BaseParamsCSV & BaseParams;
|
||||
export type TaskPayloadCSV = BaseParamsCSV & BasePayload;
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaRequest } from 'src/core/server';
|
||||
import { CancellationToken } from '../../../common';
|
||||
import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants';
|
||||
import { TaskRunResult } from '../../lib/tasks';
|
||||
import { getFieldFormats } from '../../services';
|
||||
import { ReportingRequestHandlerContext, RunTaskFnFactory } from '../../types';
|
||||
import { CsvGenerator } from '../csv_searchsource/generate_csv/generate_csv';
|
||||
import { JobParamsDownloadCSV } from './types';
|
||||
|
||||
/*
|
||||
* ImmediateExecuteFn receives the job doc payload because the payload was
|
||||
* generated in the ScheduleFn
|
||||
*/
|
||||
export type ImmediateExecuteFn = (
|
||||
jobId: null,
|
||||
job: JobParamsDownloadCSV,
|
||||
context: ReportingRequestHandlerContext,
|
||||
req: KibanaRequest
|
||||
) => Promise<TaskRunResult>;
|
||||
|
||||
export const runTaskFnFactory: RunTaskFnFactory<ImmediateExecuteFn> = function executeJobFactoryFn(
|
||||
reporting,
|
||||
parentLogger
|
||||
) {
|
||||
const config = reporting.getConfig();
|
||||
const logger = parentLogger.clone([CSV_SEARCHSOURCE_IMMEDIATE_TYPE, 'execute-job']);
|
||||
|
||||
return async function runTask(jobId, immediateJobParams, context, req) {
|
||||
const job = {
|
||||
objectType: 'immediate-search',
|
||||
...immediateJobParams,
|
||||
};
|
||||
|
||||
const savedObjectsClient = context.core.savedObjects.client;
|
||||
const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient);
|
||||
const dataPluginStart = await reporting.getDataService();
|
||||
const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings);
|
||||
|
||||
const [es, searchSourceStart] = await Promise.all([
|
||||
(await reporting.getEsClient()).asScoped(req),
|
||||
await dataPluginStart.search.searchSource.asScoped(req),
|
||||
]);
|
||||
const clients = {
|
||||
uiSettings,
|
||||
data: dataPluginStart.search.asScoped(req),
|
||||
es,
|
||||
};
|
||||
const dependencies = {
|
||||
fieldFormatsRegistry,
|
||||
searchSourceStart,
|
||||
};
|
||||
const cancellationToken = new CancellationToken();
|
||||
|
||||
const csv = new CsvGenerator(job, config, clients, dependencies, cancellationToken, logger);
|
||||
const result = await csv.generateData();
|
||||
|
||||
if (result.csv_contains_formulas) {
|
||||
logger.warn(`CSV may contain formulas whose values have been escaped`);
|
||||
}
|
||||
|
||||
if (result.max_size_reached) {
|
||||
logger.warn(`Max size reached: CSV output truncated to ${result.size} bytes`);
|
||||
}
|
||||
|
||||
const { warnings } = result;
|
||||
if (warnings) {
|
||||
warnings.forEach((warning) => {
|
||||
logger.warning(warning);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
CSV_FROM_SAVEDOBJECT_JOB_TYPE,
|
||||
CSV_SEARCHSOURCE_IMMEDIATE_TYPE,
|
||||
LICENSE_TYPE_BASIC,
|
||||
LICENSE_TYPE_ENTERPRISE,
|
||||
LICENSE_TYPE_GOLD,
|
||||
|
@ -15,7 +15,6 @@ import {
|
|||
LICENSE_TYPE_TRIAL,
|
||||
} from '../../../common/constants';
|
||||
import { ExportTypeDefinition } from '../../types';
|
||||
import { createJobFnFactory, ImmediateCreateJobFn } from './create_job';
|
||||
import { ImmediateExecuteFn, runTaskFnFactory } from './execute_job';
|
||||
import { metadata } from './metadata';
|
||||
|
||||
|
@ -23,17 +22,13 @@ import { metadata } from './metadata';
|
|||
* These functions are exported to share with the API route handler that
|
||||
* generates csv from saved object immediately on request.
|
||||
*/
|
||||
export { createJobFnFactory } from './create_job';
|
||||
export { runTaskFnFactory } from './execute_job';
|
||||
|
||||
export const getExportType = (): ExportTypeDefinition<
|
||||
ImmediateCreateJobFn,
|
||||
ImmediateExecuteFn
|
||||
> => ({
|
||||
export const getExportType = (): ExportTypeDefinition<null, ImmediateExecuteFn> => ({
|
||||
...metadata,
|
||||
jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE,
|
||||
jobType: CSV_SEARCHSOURCE_IMMEDIATE_TYPE,
|
||||
jobContentExtension: 'csv',
|
||||
createJobFnFactory,
|
||||
createJobFnFactory: null,
|
||||
runTaskFnFactory,
|
||||
validLicenses: [
|
||||
LICENSE_TYPE_TRIAL,
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CSV_SEARCHSOURCE_IMMEDIATE_TYPE } from '../../../common/constants';
|
||||
|
||||
export const metadata = {
|
||||
id: CSV_SEARCHSOURCE_IMMEDIATE_TYPE,
|
||||
name: CSV_SEARCHSOURCE_IMMEDIATE_TYPE,
|
||||
};
|
24
x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts
vendored
Normal file
24
x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/types.d.ts
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { TimeRangeParams } from '../common';
|
||||
|
||||
export interface FakeRequest {
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface JobParamsDownloadCSV {
|
||||
browserTimezone: string;
|
||||
title: string;
|
||||
searchSource: any;
|
||||
}
|
||||
|
||||
export interface SavedObjectServiceError {
|
||||
statusCode: number;
|
||||
error?: string;
|
||||
message?: string;
|
||||
}
|
|
@ -38,13 +38,17 @@ export function enqueueJobFactory(
|
|||
throw new Error(`Export type ${exportTypeId} does not exist in the registry!`);
|
||||
}
|
||||
|
||||
if (!exportType.createJobFnFactory) {
|
||||
throw new Error(`Export type ${exportTypeId} is not an async job type!`);
|
||||
}
|
||||
|
||||
const [createJob, store] = await Promise.all([
|
||||
exportType.createJobFnFactory(reporting, logger.clone([exportType.id])),
|
||||
reporting.getStore(),
|
||||
]);
|
||||
|
||||
const config = reporting.getConfig();
|
||||
const job = await createJob(jobParams, context, request);
|
||||
const job = await createJob!(jobParams, context, request);
|
||||
|
||||
// 1. Add the report to ReportingStore to show as pending
|
||||
const report = await store.addReport(
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
*/
|
||||
|
||||
import { isString } from 'lodash';
|
||||
import { getExportType as getTypeCsv } from '../export_types/csv';
|
||||
import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_from_savedobject';
|
||||
import { getExportType as getTypeCsvDeprecated } from '../export_types/csv';
|
||||
import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_searchsource_immediate';
|
||||
import { getExportType as getTypeCsv } from '../export_types/csv_searchsource';
|
||||
import { getExportType as getTypePng } from '../export_types/png';
|
||||
import { getExportType as getTypePrintablePdf } from '../export_types/printable_pdf';
|
||||
import { CreateJobFn, ExportTypeDefinition } from '../types';
|
||||
|
@ -82,8 +83,9 @@ export function getExportTypesRegistry(): ExportTypesRegistry {
|
|||
const registry = new ExportTypesRegistry();
|
||||
type CreateFnType = CreateJobFn<any, any>; // can not specify params types because different type of params are not assignable to each other
|
||||
type RunFnType = any; // can not specify because ImmediateExecuteFn is not assignable to RunTaskFn
|
||||
const getTypeFns: Array<() => ExportTypeDefinition<CreateFnType, RunFnType>> = [
|
||||
const getTypeFns: Array<() => ExportTypeDefinition<CreateFnType | null, RunFnType>> = [
|
||||
getTypeCsv,
|
||||
getTypeCsvDeprecated,
|
||||
getTypeCsvFromSavedObject,
|
||||
getTypePng,
|
||||
getTypePrintablePdf,
|
||||
|
|
|
@ -122,6 +122,8 @@ export class ReportingPlugin
|
|||
savedObjects: core.savedObjects,
|
||||
uiSettings: core.uiSettings,
|
||||
store,
|
||||
esClient: core.elasticsearch.client,
|
||||
data: plugins.data,
|
||||
taskManager: plugins.taskManager,
|
||||
});
|
||||
|
||||
|
|
|
@ -8,26 +8,17 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { KibanaRequest } from 'src/core/server';
|
||||
import { ReportingCore } from '../';
|
||||
import { createJobFnFactory } from '../export_types/csv_from_savedobject/create_job';
|
||||
import { runTaskFnFactory } from '../export_types/csv_from_savedobject/execute_job';
|
||||
import {
|
||||
JobParamsPanelCsv,
|
||||
JobParamsPanelCsvPost,
|
||||
} from '../export_types/csv_from_savedobject/types';
|
||||
import { runTaskFnFactory } from '../export_types/csv_searchsource_immediate/execute_job';
|
||||
import { JobParamsDownloadCSV } from '../export_types/csv_searchsource_immediate/types';
|
||||
import { LevelLogger as Logger } from '../lib';
|
||||
import { TaskRunResult } from '../lib/tasks';
|
||||
import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing';
|
||||
import { getJobParamsFromRequest } from './lib/get_job_params_from_request';
|
||||
import { HandlerErrorFunction } from './types';
|
||||
|
||||
const API_BASE_URL_V1 = '/api/reporting/v1';
|
||||
const API_BASE_GENERATE_V1 = `${API_BASE_URL_V1}/generate`;
|
||||
|
||||
export type CsvFromSavedObjectRequest = KibanaRequest<
|
||||
JobParamsPanelCsv,
|
||||
unknown,
|
||||
JobParamsPanelCsvPost
|
||||
>;
|
||||
export type CsvFromSavedObjectRequest = KibanaRequest<unknown, unknown, JobParamsDownloadCSV>;
|
||||
|
||||
/*
|
||||
* This function registers API Endpoints for immediate Reporting jobs. The API inputs are:
|
||||
|
@ -47,43 +38,28 @@ export function registerGenerateCsvFromSavedObjectImmediate(
|
|||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
|
||||
/*
|
||||
* CSV export with the `immediate` option does not queue a job with Reporting's ESQueue to run the job async. Instead, this does:
|
||||
* - re-use the createJob function to build up es query config
|
||||
* - re-use the runTask function to run the scan and scroll queries and capture the entire CSV in a result object.
|
||||
*/
|
||||
// This API calls run the SearchSourceImmediate export type's runTaskFn directly
|
||||
router.post(
|
||||
{
|
||||
path: `${API_BASE_GENERATE_V1}/immediate/csv/saved-object/{savedObjectType}:{savedObjectId}`,
|
||||
path: `${API_BASE_GENERATE_V1}/immediate/csv_searchsource`,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
savedObjectType: schema.string({ minLength: 5 }),
|
||||
savedObjectId: schema.string({ minLength: 5 }),
|
||||
}),
|
||||
body: schema.object({
|
||||
state: schema.object({}, { unknowns: 'allow' }),
|
||||
timerange: schema.object({
|
||||
timezone: schema.string({ defaultValue: 'UTC' }),
|
||||
min: schema.nullable(schema.oneOf([schema.number(), schema.string({ minLength: 5 })])),
|
||||
max: schema.nullable(schema.oneOf([schema.number(), schema.string({ minLength: 5 })])),
|
||||
}),
|
||||
searchSource: schema.object({}, { unknowns: 'allow' }),
|
||||
browserTimezone: schema.string({ defaultValue: 'UTC' }),
|
||||
title: schema.string(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
userHandler(async (user, context, req: CsvFromSavedObjectRequest, res) => {
|
||||
const logger = parentLogger.clone(['savedobject-csv']);
|
||||
const jobParams = getJobParamsFromRequest(req);
|
||||
const createJob = createJobFnFactory(reporting, logger);
|
||||
const logger = parentLogger.clone(['csv_searchsource_immediate']);
|
||||
const runTaskFn = runTaskFnFactory(reporting, logger);
|
||||
|
||||
try {
|
||||
// FIXME: no create job for immediate download
|
||||
const payload = await createJob(jobParams, context, req);
|
||||
const {
|
||||
content_type: jobOutputContentType,
|
||||
content: jobOutputContent,
|
||||
size: jobOutputSize,
|
||||
}: TaskRunResult = await runTaskFn(null, payload, context, req);
|
||||
}: TaskRunResult = await runTaskFn(null, req.body, context, req);
|
||||
|
||||
logger.info(`Job output size: ${jobOutputSize} bytes`);
|
||||
|
|
@ -13,7 +13,7 @@ import { API_BASE_URL } from '../../common/constants';
|
|||
import { LevelLogger as Logger } from '../lib';
|
||||
import { enqueueJobFactory } from '../lib/enqueue_job';
|
||||
import { registerGenerateFromJobParams } from './generate_from_jobparams';
|
||||
import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate';
|
||||
import { registerGenerateCsvFromSavedObjectImmediate } from './csv_searchsource_immediate';
|
||||
import { HandlerFunction } from './types';
|
||||
|
||||
const esErrors = elasticsearchErrors as Record<string, any>;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// @ts-ignore
|
||||
import contentDisposition from 'content-disposition';
|
||||
import { get } from 'lodash';
|
||||
import { CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants';
|
||||
import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '../../../common/constants';
|
||||
import { ExportTypesRegistry, statuses } from '../../lib';
|
||||
import { ReportDocument } from '../../lib/store';
|
||||
import { TaskRunResult } from '../../lib/tasks';
|
||||
|
@ -34,7 +34,7 @@ const getTitle = (exportType: ExportTypeDefinition, title?: string): string =>
|
|||
const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeDefinition) => {
|
||||
const metaDataHeaders: Record<string, boolean> = {};
|
||||
|
||||
if (exportType.jobType === CSV_JOB_TYPE_DEPRECATED) {
|
||||
if (exportType.jobType === CSV_JOB_TYPE || exportType.jobType === CSV_JOB_TYPE_DEPRECATED) {
|
||||
const csvContainsFormulas = get(output, 'csv_contains_formulas', false);
|
||||
const maxSizedReach = get(output, 'max_size_reached', false);
|
||||
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { JobParamsPanelCsv } from '../../export_types/csv_from_savedobject/types';
|
||||
import { CsvFromSavedObjectRequest } from '../generate_from_savedobject_immediate';
|
||||
|
||||
export function getJobParamsFromRequest(request: CsvFromSavedObjectRequest): JobParamsPanelCsv {
|
||||
const { savedObjectType, savedObjectId } = request.params;
|
||||
const { timerange, state } = request.body;
|
||||
|
||||
const post = timerange || state ? { timerange, state } : undefined;
|
||||
|
||||
return {
|
||||
savedObjectType,
|
||||
savedObjectId,
|
||||
post,
|
||||
};
|
||||
}
|
|
@ -11,7 +11,10 @@ jest.mock('../browsers');
|
|||
|
||||
import _ from 'lodash';
|
||||
import * as Rx from 'rxjs';
|
||||
import { coreMock } from 'src/core/server/mocks';
|
||||
import { coreMock, elasticsearchServiceMock } from 'src/core/server/mocks';
|
||||
import { fieldFormats } from 'src/plugins/data/server';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { dataPluginMock } from 'src/plugins/data/server/mocks';
|
||||
import { ReportingConfig, ReportingCore } from '../';
|
||||
import { featuresPluginMock } from '../../../features/server/mocks';
|
||||
import {
|
||||
|
@ -22,6 +25,7 @@ import {
|
|||
import { ReportingConfigType } from '../config';
|
||||
import { ReportingInternalSetup, ReportingInternalStart } from '../core';
|
||||
import { ReportingStore } from '../lib';
|
||||
import { setFieldFormats } from '../services';
|
||||
import { createMockLevelLogger } from './create_mock_levellogger';
|
||||
|
||||
(initializeBrowserDriverFactory as jest.Mock<
|
||||
|
@ -45,15 +49,22 @@ export const createMockPluginSetup = (setupMock?: any): ReportingInternalSetup =
|
|||
|
||||
const logger = createMockLevelLogger();
|
||||
|
||||
const createMockPluginStart = (
|
||||
mockReportingCore: ReportingCore,
|
||||
const createMockReportingStore = () => ({} as ReportingStore);
|
||||
|
||||
export const createMockPluginStart = (
|
||||
mockReportingCore: ReportingCore | undefined,
|
||||
startMock?: any
|
||||
): ReportingInternalStart => {
|
||||
const store = new ReportingStore(mockReportingCore, logger);
|
||||
const store = mockReportingCore
|
||||
? new ReportingStore(mockReportingCore, logger)
|
||||
: createMockReportingStore();
|
||||
|
||||
return {
|
||||
browserDriverFactory: startMock.browserDriverFactory,
|
||||
esClient: elasticsearchServiceMock.createClusterClient(),
|
||||
savedObjects: startMock.savedObjects || { getScopedClient: jest.fn() },
|
||||
uiSettings: startMock.uiSettings || { asScopedToClient: () => ({ get: jest.fn() }) },
|
||||
data: startMock.data || dataPluginMock.createStartContract(),
|
||||
store,
|
||||
taskManager: {
|
||||
schedule: jest.fn().mockImplementation(() => ({ id: 'taskId' })),
|
||||
|
@ -124,11 +135,18 @@ export const createMockReportingCore = async (
|
|||
setupDepsMock: ReportingInternalSetup | undefined = undefined,
|
||||
startDepsMock: ReportingInternalStart | undefined = undefined
|
||||
) => {
|
||||
config = config || {};
|
||||
const mockReportingCore = ({
|
||||
getConfig: () => config,
|
||||
getElasticsearchService: () => setupDepsMock?.elasticsearch,
|
||||
getDataService: () => startDepsMock?.data,
|
||||
} as unknown) as ReportingCore;
|
||||
|
||||
if (!setupDepsMock) {
|
||||
setupDepsMock = createMockPluginSetup({});
|
||||
}
|
||||
if (!startDepsMock) {
|
||||
startDepsMock = createMockPluginStart(mockReportingCore, {});
|
||||
}
|
||||
|
||||
const context = coreMock.createPluginInitializerContext(createMockConfigSchema());
|
||||
const core = new ReportingCore(logger, context);
|
||||
|
@ -143,5 +161,12 @@ export const createMockReportingCore = async (
|
|||
await core.pluginStart(startDepsMock);
|
||||
await core.pluginStartsUp();
|
||||
|
||||
setFieldFormats({
|
||||
fieldFormatServiceFactory() {
|
||||
const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry();
|
||||
return Promise.resolve(fieldFormatsRegistry);
|
||||
},
|
||||
});
|
||||
|
||||
return core;
|
||||
};
|
||||
|
|
|
@ -83,13 +83,16 @@ export type RunTaskFnFactory<RunTaskFnType> = (
|
|||
logger: LevelLogger
|
||||
) => RunTaskFnType;
|
||||
|
||||
export interface ExportTypeDefinition<CreateJobFnType = CreateJobFn, RunTaskFnType = RunTaskFn> {
|
||||
export interface ExportTypeDefinition<
|
||||
CreateJobFnType = CreateJobFn | null,
|
||||
RunTaskFnType = RunTaskFn
|
||||
> {
|
||||
id: string;
|
||||
name: string;
|
||||
jobType: string;
|
||||
jobContentEncoding?: string;
|
||||
jobContentExtension: string;
|
||||
createJobFnFactory: CreateJobFnFactory<CreateJobFnType>;
|
||||
createJobFnFactory: CreateJobFnFactory<CreateJobFnType> | null; // immediate job does not have a "create" phase
|
||||
runTaskFnFactory: RunTaskFnFactory<RunTaskFnType>;
|
||||
validLicenses: string[];
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
|
@ -24,6 +28,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
|
@ -75,6 +83,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
|
@ -86,6 +98,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"dashboard": 0,
|
||||
|
@ -166,6 +182,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"enabled": true,
|
||||
"last7Days": Object {
|
||||
"PNG": Object {
|
||||
|
@ -177,6 +197,10 @@ Object {
|
|||
"available": true,
|
||||
"total": 1,
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": true,
|
||||
"total": 0,
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": 1,
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
*/
|
||||
|
||||
import { uniq } from 'lodash';
|
||||
import { CSV_JOB_TYPE_DEPRECATED, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants';
|
||||
import {
|
||||
CSV_JOB_TYPE,
|
||||
CSV_JOB_TYPE_DEPRECATED,
|
||||
PDF_JOB_TYPE,
|
||||
PNG_JOB_TYPE,
|
||||
} from '../../common/constants';
|
||||
import { AvailableTotal, ExportType, FeatureAvailabilityMap, RangeStats } from './types';
|
||||
|
||||
function getForFeature(
|
||||
|
@ -55,6 +60,7 @@ export const decorateRangeStats = (
|
|||
|
||||
// combine the known types with any unknown type found in reporting data
|
||||
const keysBasic = uniq([
|
||||
CSV_JOB_TYPE,
|
||||
CSV_JOB_TYPE_DEPRECATED,
|
||||
PNG_JOB_TYPE,
|
||||
...Object.keys(rangeStatsBasic),
|
||||
|
|
|
@ -354,11 +354,13 @@ describe('data modeling', () => {
|
|||
available: true,
|
||||
browser_type: 'chromium',
|
||||
csv: { available: true, total: 4 },
|
||||
csv_searchsource: { available: true, total: 4 },
|
||||
enabled: true,
|
||||
last7Days: {
|
||||
PNG: { available: true, total: 0 },
|
||||
_all: 0,
|
||||
csv: { available: true, total: 0 },
|
||||
csv_searchsource: { available: true, total: 0 },
|
||||
printable_pdf: {
|
||||
app: { dashboard: 0, visualization: 0 },
|
||||
available: true,
|
||||
|
@ -389,11 +391,13 @@ describe('data modeling', () => {
|
|||
available: true,
|
||||
browser_type: 'chromium',
|
||||
csv: { available: true, total: 0 },
|
||||
csv_searchsource: { available: true, total: 0 },
|
||||
enabled: true,
|
||||
last7Days: {
|
||||
PNG: { available: true, total: 3 },
|
||||
_all: 4,
|
||||
csv: { available: true, total: 0 },
|
||||
csv_searchsource: { available: true, total: 0 },
|
||||
printable_pdf: {
|
||||
app: { 'canvas workpad': 1, dashboard: 0, visualization: 0 },
|
||||
available: true,
|
||||
|
@ -431,6 +435,7 @@ describe('data modeling', () => {
|
|||
layout: { preserve_layout: 0, print: 0 },
|
||||
},
|
||||
csv: { available: true, total: 0 },
|
||||
csv_searchsource: { available: true, total: 0 },
|
||||
PNG: { available: true, total: 0 },
|
||||
},
|
||||
_all: 0,
|
||||
|
@ -443,6 +448,7 @@ describe('data modeling', () => {
|
|||
layout: { preserve_layout: 0, print: 0 },
|
||||
},
|
||||
csv: { available: true, total: 0 },
|
||||
csv_searchsource: { available: true, total: 0 },
|
||||
PNG: { available: true, total: 0 },
|
||||
});
|
||||
});
|
||||
|
@ -491,6 +497,14 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": Object {
|
||||
"type": "boolean",
|
||||
},
|
||||
"total": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"enabled": Object {
|
||||
"type": "boolean",
|
||||
},
|
||||
|
@ -514,6 +528,14 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"available": Object {
|
||||
"type": "boolean",
|
||||
},
|
||||
"total": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"app": Object {
|
||||
"canvas workpad": Object {
|
||||
|
@ -585,6 +607,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -620,6 +653,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -655,6 +699,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -690,6 +745,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -725,6 +791,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -760,6 +837,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -845,6 +933,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -880,6 +979,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -915,6 +1025,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -950,6 +1071,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -985,6 +1117,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
@ -1020,6 +1163,17 @@ describe('Ready for collection observable', () => {
|
|||
"type": "long",
|
||||
},
|
||||
},
|
||||
"csv_searchsource": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"dashboard": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"visualization": Object {
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
"printable_pdf": Object {
|
||||
"canvas workpad": Object {
|
||||
"type": "long",
|
||||
|
|
|
@ -16,6 +16,7 @@ const appCountsSchema: MakeSchemaFrom<AppCounts> = {
|
|||
|
||||
const byAppCountsSchema: MakeSchemaFrom<RangeStats['statuses']['cancelled']> = {
|
||||
csv: appCountsSchema,
|
||||
csv_searchsource: appCountsSchema,
|
||||
PNG: appCountsSchema,
|
||||
printable_pdf: appCountsSchema,
|
||||
};
|
||||
|
@ -27,6 +28,7 @@ const availableTotalSchema: MakeSchemaFrom<AvailableTotal> = {
|
|||
|
||||
const jobTypesSchema: MakeSchemaFrom<JobTypes> = {
|
||||
csv: availableTotalSchema,
|
||||
csv_searchsource: availableTotalSchema,
|
||||
PNG: availableTotalSchema,
|
||||
printable_pdf: {
|
||||
...availableTotalSchema,
|
||||
|
|
|
@ -59,7 +59,7 @@ export interface AvailableTotal {
|
|||
total: number;
|
||||
}
|
||||
|
||||
type BaseJobTypes = 'csv' | 'PNG' | 'printable_pdf';
|
||||
type BaseJobTypes = 'csv' | 'csv_searchsource' | 'PNG' | 'printable_pdf';
|
||||
export interface LayoutCounts {
|
||||
print: number;
|
||||
preserve_layout: number;
|
||||
|
@ -106,7 +106,7 @@ export type ReportingUsageType = RangeStats & {
|
|||
last7Days: RangeStats;
|
||||
};
|
||||
|
||||
export type ExportType = 'csv' | 'printable_pdf' | 'PNG';
|
||||
export type ExportType = 'csv' | 'csv_searchsource' | 'printable_pdf' | 'PNG';
|
||||
export type FeatureAvailabilityMap = { [F in ExportType]: boolean };
|
||||
|
||||
export interface KeyCountBucket {
|
||||
|
|
|
@ -2436,6 +2436,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"available": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"total": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"available": {
|
||||
|
@ -2521,6 +2531,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2564,6 +2587,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2607,6 +2643,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2650,6 +2699,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2693,6 +2755,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2736,6 +2811,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2787,6 +2875,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"available": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"total": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"available": {
|
||||
|
@ -2872,6 +2970,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2915,6 +3026,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -2958,6 +3082,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -3001,6 +3138,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -3044,6 +3194,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
@ -3087,6 +3250,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"csv_searchsource": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
"type": "long"
|
||||
},
|
||||
"dashboard": {
|
||||
"type": "long"
|
||||
},
|
||||
"visualization": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PNG": {
|
||||
"properties": {
|
||||
"canvas workpad": {
|
||||
|
|
53
x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap
generated
Normal file
53
x-pack/test/functional/apps/dashboard/reporting/__snapshots__/download_csv.snap
generated
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`dashboard Reporting Download CSV E-Commerce Data Download CSV export of a saved search panel 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`dashboard Reporting Download CSV E-Commerce Data Downloads a filtered CSV export of a saved search panel 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`dashboard Reporting Download CSV Field Formatters and Scripted Fields Download CSV export of a saved search panel 1`] = `
|
||||
"date,\\"_id\\",name,gender,value,year,\\"years_ago\\",\\"date_informal\\"
|
||||
\\"Jan 1, 1984 @ 00:00:00.000\\",\\"1984-Fethany-F\\",Fethany,F,5,1984,\\"35.00000000000000000000\\",\\"Jan 1st 84\\"
|
||||
\\"Jan 1, 1983 @ 00:00:00.000\\",\\"1983-Fethany-F\\",Fethany,F,\\"1,043\\",1983,\\"36.00000000000000000000\\",\\"Jan 1st 83\\"
|
||||
\\"Jan 1, 1982 @ 00:00:00.000\\",\\"1982-Fethany-F\\",Fethany,F,780,1982,\\"37.00000000000000000000\\",\\"Jan 1st 82\\"
|
||||
\\"Jan 1, 1981 @ 00:00:00.000\\",\\"1981-Fethany-F\\",Fethany,F,655,1981,\\"38.00000000000000000000\\",\\"Jan 1st 81\\"
|
||||
\\"Jan 1, 1980 @ 00:00:00.000\\",\\"1980-Fethany-F\\",Fethany,F,702,1980,\\"39.00000000000000000000\\",\\"Jan 1st 80\\"
|
||||
"
|
||||
`;
|
|
@ -9,91 +9,139 @@ import { REPO_ROOT } from '@kbn/utils';
|
|||
import expect from '@kbn/expect';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as Rx from 'rxjs';
|
||||
import { filter, first, map, timeout } from 'rxjs/operators';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
const csvPath = path.resolve(REPO_ROOT, 'target/functional-tests/downloads/Ecommerce Data.csv');
|
||||
|
||||
// checks every 100ms for the file to exist in the download dir
|
||||
// just wait up to 5 seconds
|
||||
const getDownload$ = (filePath: string) => {
|
||||
return Rx.interval(100).pipe(
|
||||
map(() => fs.existsSync(filePath)),
|
||||
filter((value) => value === true),
|
||||
first(),
|
||||
timeout(5000)
|
||||
);
|
||||
};
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const browser = getService('browser');
|
||||
const dashboardPanelActions = getService('dashboardPanelActions');
|
||||
const log = getService('log');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const filterBar = getService('filterBar');
|
||||
const find = getService('find');
|
||||
const PageObjects = getPageObjects(['reporting', 'common', 'dashboard']);
|
||||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['reporting', 'common', 'dashboard', 'timePicker']);
|
||||
|
||||
const getCsvPath = (name: string) =>
|
||||
path.resolve(REPO_ROOT, `target/functional-tests/downloads/${name}.csv`);
|
||||
|
||||
// checks every 100ms for the file to exist in the download dir
|
||||
// just wait up to 5 seconds
|
||||
const getDownload = (filePath: string) => {
|
||||
return retry.tryForTime(5000, async () => {
|
||||
expect(fs.existsSync(filePath)).to.be(true);
|
||||
return fs.readFileSync(filePath).toString();
|
||||
});
|
||||
};
|
||||
|
||||
const clickActionsMenu = async (headingTestSubj: string) => {
|
||||
const savedSearchPanel = await testSubjects.find('embeddablePanelHeading-' + headingTestSubj);
|
||||
await dashboardPanelActions.toggleContextMenu(savedSearchPanel);
|
||||
};
|
||||
|
||||
const clickDownloadCsv = async () => {
|
||||
log.debug('click "More"');
|
||||
await dashboardPanelActions.clickContextMenuMoreItem();
|
||||
|
||||
const actionItemTestSubj = 'embeddablePanelAction-downloadCsvReport';
|
||||
await testSubjects.existOrFail(actionItemTestSubj); // wait for the full panel to display or else the test runner could click the wrong option!
|
||||
log.debug('click "Download CSV"');
|
||||
await testSubjects.click(actionItemTestSubj);
|
||||
await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel
|
||||
};
|
||||
|
||||
describe('Download CSV', () => {
|
||||
before('initialize tests', async () => {
|
||||
log.debug('ReportingPage:initTests');
|
||||
await esArchiver.loadIfNeeded('reporting/ecommerce');
|
||||
await esArchiver.loadIfNeeded('reporting/ecommerce_kibana');
|
||||
await browser.setWindowSize(1600, 850);
|
||||
});
|
||||
|
||||
after('clean up archives and previous file download', async () => {
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
|
||||
afterEach('remove download', () => {
|
||||
try {
|
||||
fs.unlinkSync(csvPath);
|
||||
fs.unlinkSync(getCsvPath('Ecommerce Data'));
|
||||
} catch (e) {
|
||||
// it might not have been there to begin with
|
||||
}
|
||||
});
|
||||
|
||||
it('Downloads a CSV export of a saved search panel', async function () {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||
const savedSearchPanel = await testSubjects.find('embeddablePanelHeading-EcommerceData');
|
||||
await dashboardPanelActions.toggleContextMenu(savedSearchPanel);
|
||||
describe('E-Commerce Data', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
|
||||
const actionExists = await testSubjects.exists('embeddablePanelAction-downloadCsvReport');
|
||||
if (!actionExists) {
|
||||
await dashboardPanelActions.clickContextMenuMoreItem();
|
||||
}
|
||||
await testSubjects.existOrFail('embeddablePanelAction-downloadCsvReport'); // wait for the full panel to display or else the test runner could click the wrong option!
|
||||
await testSubjects.click('embeddablePanelAction-downloadCsvReport');
|
||||
await testSubjects.existOrFail('csvDownloadStarted'); // validate toast panel
|
||||
it('Download CSV export of a saved search panel', async function () {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||
await clickActionsMenu('EcommerceData');
|
||||
await clickDownloadCsv();
|
||||
|
||||
const fileExists = await getDownload$(csvPath).toPromise();
|
||||
expect(fileExists).to.be(true);
|
||||
const csvFile = await getDownload(getCsvPath('Ecommerce Data'));
|
||||
expectSnapshot(csvFile).toMatch();
|
||||
});
|
||||
|
||||
// no need to validate download contents, API Integration tests do that some different variations
|
||||
it('Downloads a filtered CSV export of a saved search panel', async function () {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard');
|
||||
|
||||
// add a filter
|
||||
await filterBar.addFilter('currency', 'is', 'EUR');
|
||||
|
||||
await clickActionsMenu('EcommerceData');
|
||||
await clickDownloadCsv();
|
||||
|
||||
const csvFile = await getDownload(getCsvPath('Ecommerce Data'));
|
||||
expectSnapshot(csvFile).toMatch();
|
||||
});
|
||||
|
||||
it('Gets the correct filename if panel titles are hidden', async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard Hidden Panel Titles');
|
||||
const savedSearchPanel = await find.byCssSelector(
|
||||
'[data-test-embeddable-id="94eab06f-60ac-4a85-b771-3a8ed475c9bb"]'
|
||||
); // panel title is hidden
|
||||
await dashboardPanelActions.toggleContextMenu(savedSearchPanel);
|
||||
|
||||
await clickDownloadCsv();
|
||||
await testSubjects.existOrFail('csvDownloadStarted');
|
||||
|
||||
const csvFile = await getDownload(getCsvPath('Ecommerce Data')); // file exists with proper name
|
||||
expect(csvFile).to.not.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('Gets the correct filename if panel titles are hidden', async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('Ecom Dashboard Hidden Panel Titles');
|
||||
const savedSearchPanel = await find.byCssSelector(
|
||||
'[data-test-embeddable-id="94eab06f-60ac-4a85-b771-3a8ed475c9bb"]'
|
||||
); // panel title is hidden
|
||||
await dashboardPanelActions.toggleContextMenu(savedSearchPanel);
|
||||
describe('Field Formatters and Scripted Fields', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('reporting/hugedata');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('reporting/hugedata');
|
||||
});
|
||||
|
||||
const actionExists = await testSubjects.exists('embeddablePanelAction-downloadCsvReport');
|
||||
if (!actionExists) {
|
||||
await dashboardPanelActions.clickContextMenuMoreItem();
|
||||
}
|
||||
await testSubjects.existOrFail('embeddablePanelAction-downloadCsvReport');
|
||||
await testSubjects.click('embeddablePanelAction-downloadCsvReport');
|
||||
await testSubjects.existOrFail('csvDownloadStarted');
|
||||
it('Download CSV export of a saved search panel', async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
await PageObjects.dashboard.loadSavedDashboard('names dashboard');
|
||||
await PageObjects.timePicker.setAbsoluteRange(
|
||||
'Jan 01, 1980 @ 00:00:00.000',
|
||||
'Dec 31, 1984 @ 23:59:59.000'
|
||||
);
|
||||
|
||||
const fileExists = await getDownload$(csvPath).toPromise(); // file exists with proper name
|
||||
expect(fileExists).to.be(true);
|
||||
await PageObjects.common.sleep(1000);
|
||||
|
||||
await filterBar.addFilter('name.keyword', 'is', 'Fethany');
|
||||
|
||||
await PageObjects.common.sleep(1000);
|
||||
|
||||
await clickActionsMenu('namessearch');
|
||||
await clickDownloadCsv();
|
||||
|
||||
const csvFile = await getDownload(getCsvPath('namessearch'));
|
||||
expectSnapshot(csvFile).toMatch();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,40 +1,88 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`discover Discover Generate CSV: archived search generates a report with data 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"order_date\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\",\\"\\"Women's Accessories\\"\\",\\"\\"Men's Accessories\\"\\"]\\",EUR,19,716724,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0687606876\\"\\",\\"\\"ZO0290502905\\"\\",\\"\\"ZO0126701267\\"\\",\\"\\"ZO0308503085\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Shoes\\"\\",\\"\\"Women's Clothing\\"\\"]\\",EUR,45,591503,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0006400064\\"\\",\\"\\"ZO0150601506\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0638206382\\"\\",\\"\\"ZO0038800388\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0297602976\\"\\",\\"\\"ZO0565605656\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0561405614\\"\\",\\"\\"ZO0281602816\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\"]\\",EUR,41,591636,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0385003850\\"\\",\\"\\"ZO0408604086\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0505605056\\"\\",\\"\\"ZO0513605136\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0276702767\\"\\",\\"\\"ZO0291702917\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0046600466\\"\\",\\"\\"ZO0050800508\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Clothing\\"\\",\\"\\"Men's Shoes\\"\\"]\\",EUR,48,590970,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0455604556\\"\\",\\"\\"ZO0680806808\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Clothing\\"\\",\\"\\"Women's Shoes\\"\\"]\\",EUR,46,591299,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0229002290\\"\\",\\"\\"ZO0674406744\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0529905299\\"\\",\\"\\"ZO0617006170\\"\\"]\\"
|
||||
exports[`discover Discover CSV Export Generate CSV: archived search generates a report with data 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`discover Discover Generate CSV: archived search generates a report with filtered data 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"order_date\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\",\\"\\"Women's Accessories\\"\\",\\"\\"Men's Accessories\\"\\"]\\",EUR,19,716724,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0687606876\\"\\",\\"\\"ZO0290502905\\"\\",\\"\\"ZO0126701267\\"\\",\\"\\"ZO0308503085\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Shoes\\"\\",\\"\\"Women's Clothing\\"\\"]\\",EUR,45,591503,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0006400064\\"\\",\\"\\"ZO0150601506\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0638206382\\"\\",\\"\\"ZO0038800388\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0297602976\\"\\",\\"\\"ZO0565605656\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0561405614\\"\\",\\"\\"ZO0281602816\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Shoes\\"\\",\\"\\"Men's Clothing\\"\\"]\\",EUR,41,591636,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0385003850\\"\\",\\"\\"ZO0408604086\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0505605056\\"\\",\\"\\"ZO0513605136\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0276702767\\"\\",\\"\\"ZO0291702917\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0046600466\\"\\",\\"\\"ZO0050800508\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Men's Clothing\\"\\",\\"\\"Men's Shoes\\"\\"]\\",EUR,48,590970,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0455604556\\"\\",\\"\\"ZO0680806808\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Women's Clothing\\"\\",\\"\\"Women's Shoes\\"\\"]\\",EUR,46,591299,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0229002290\\"\\",\\"\\"ZO0674406744\\"\\"]\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Jul 12, 2019 @ 00:00:00.000\\",\\"[\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\",\\"\\"Dec 31, 2016 @ 00:00:00.000\\"\\"]\\",\\"[\\"\\"ZO0529905299\\"\\",\\"\\"ZO0617006170\\"\\"]\\"
|
||||
exports[`discover Discover CSV Export Generate CSV: archived search generates a report with discover:searchFieldsFromSource = true 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`discover Discover Generate CSV: new search generates a report with data 1`] = `
|
||||
exports[`discover Discover CSV Export Generate CSV: archived search generates a report with filtered data 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`discover Discover CSV Export Generate CSV: new search generates a report from a new search with data: default 1`] = `
|
||||
"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user
|
||||
3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
54.4,
|
||||
24.5
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`discover Discover CSV Export Generate CSV: new search generates a report from a new search with data: discover:searchFieldsFromSource 1`] = `
|
||||
"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user
|
||||
3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
54.4,
|
||||
24.5
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan
|
||||
"
|
||||
`;
|
||||
|
|
|
@ -12,18 +12,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const log = getService('log');
|
||||
const es = getService('es');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const browser = getService('browser');
|
||||
const PageObjects = getPageObjects(['reporting', 'common', 'discover', 'timePicker']);
|
||||
const filterBar = getService('filterBar');
|
||||
|
||||
describe('Discover', () => {
|
||||
const setFieldsFromSource = async (setValue: boolean) => {
|
||||
await kibanaServer.uiSettings.update({ 'discover:searchFieldsFromSource': setValue });
|
||||
};
|
||||
|
||||
describe('Discover CSV Export', () => {
|
||||
before('initialize tests', async () => {
|
||||
log.debug('ReportingPage:initTests');
|
||||
await esArchiver.loadIfNeeded('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
await browser.setWindowSize(1600, 850);
|
||||
});
|
||||
after('clean up archives', async () => {
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
await es.deleteByQuery({
|
||||
index: '.reporting-*',
|
||||
refresh: true,
|
||||
|
@ -31,7 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Generate CSV: new search', () => {
|
||||
describe('Check Available', () => {
|
||||
beforeEach(() => PageObjects.common.navigateToApp('discover'));
|
||||
|
||||
it('is not available if new', async () => {
|
||||
|
@ -63,8 +70,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.reporting.openCsvReportingPanel();
|
||||
expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('generates a report with data', async () => {
|
||||
describe('Generate CSV: new search', () => {
|
||||
beforeEach(async () => {
|
||||
await esArchiver.load('reporting/ecommerce_kibana'); // reload the archive to wipe out changes made by each test
|
||||
await PageObjects.common.navigateToApp('discover');
|
||||
});
|
||||
|
||||
it('generates a report from a new search with data: default', async () => {
|
||||
await PageObjects.discover.clickNewSearchButton();
|
||||
await PageObjects.reporting.setTimepickerInDataRange();
|
||||
await PageObjects.discover.saveSearch('my search - with data - expectReportCanBeCreated');
|
||||
|
@ -79,6 +93,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expectSnapshot(res.text).toMatch();
|
||||
});
|
||||
|
||||
it('generates a report from a new search with data: discover:searchFieldsFromSource', async () => {
|
||||
await setFieldsFromSource(true);
|
||||
await PageObjects.discover.clickNewSearchButton();
|
||||
await PageObjects.reporting.setTimepickerInDataRange();
|
||||
await PageObjects.discover.saveSearch(
|
||||
'my search - with fieldsFromSource data - expectReportCanBeCreated'
|
||||
);
|
||||
await PageObjects.reporting.openCsvReportingPanel();
|
||||
await PageObjects.reporting.clickGenerateReportButton();
|
||||
|
||||
const url = await PageObjects.reporting.getReportURL(60000);
|
||||
const res = await PageObjects.reporting.getResponse(url);
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.get('content-type')).to.equal('text/csv; charset=utf-8');
|
||||
expectSnapshot(res.text).toMatch();
|
||||
await setFieldsFromSource(false);
|
||||
});
|
||||
|
||||
it('generates a report with no data', async () => {
|
||||
await PageObjects.reporting.setTimepickerInNoDataRange();
|
||||
await PageObjects.discover.saveSearch('my search - no data - expectReportCanBeCreated');
|
||||
|
@ -98,6 +131,24 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
describe('Generate CSV: archived search', () => {
|
||||
const setupPage = async () => {
|
||||
const fromTime = 'Apr 27, 2019 @ 23:56:51.374';
|
||||
const toTime = 'Aug 23, 2019 @ 16:18:51.821';
|
||||
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
|
||||
};
|
||||
|
||||
const getReport = async () => {
|
||||
await PageObjects.reporting.openCsvReportingPanel();
|
||||
await PageObjects.reporting.clickGenerateReportButton();
|
||||
|
||||
const url = await PageObjects.reporting.getReportURL(60000);
|
||||
const res = await PageObjects.reporting.getResponse(url);
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.get('content-type')).to.equal('text/csv; charset=utf-8');
|
||||
return res;
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
@ -111,41 +162,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
beforeEach(() => PageObjects.common.navigateToApp('discover'));
|
||||
|
||||
it('generates a report with data', async () => {
|
||||
await setupPage();
|
||||
await PageObjects.discover.loadSavedSearch('Ecommerce Data');
|
||||
const fromTime = 'Apr 27, 2019 @ 23:56:51.374';
|
||||
const toTime = 'Aug 23, 2019 @ 16:18:51.821';
|
||||
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
|
||||
|
||||
await PageObjects.reporting.openCsvReportingPanel();
|
||||
await PageObjects.reporting.clickGenerateReportButton();
|
||||
|
||||
const url = await PageObjects.reporting.getReportURL(60000);
|
||||
const res = await PageObjects.reporting.getResponse(url);
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.get('content-type')).to.equal('text/csv; charset=utf-8');
|
||||
expectSnapshot(res.text).toMatch();
|
||||
const { text } = await getReport();
|
||||
expectSnapshot(text).toMatch();
|
||||
});
|
||||
|
||||
it('generates a report with filtered data', async () => {
|
||||
await setupPage();
|
||||
await PageObjects.discover.loadSavedSearch('Ecommerce Data');
|
||||
const fromTime = 'Apr 27, 2019 @ 23:56:51.374';
|
||||
const toTime = 'Aug 23, 2019 @ 16:18:51.821';
|
||||
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
|
||||
|
||||
// filter and re-save
|
||||
await filterBar.addFilter('currency', 'is', 'EUR');
|
||||
await PageObjects.discover.saveSearch(`Ecommerce Data: EUR Filtered`);
|
||||
await PageObjects.discover.saveSearch(`Ecommerce Data: EUR Filtered`); // renamed the search
|
||||
|
||||
await PageObjects.reporting.openCsvReportingPanel();
|
||||
await PageObjects.reporting.clickGenerateReportButton();
|
||||
const { text } = await getReport();
|
||||
expectSnapshot(text).toMatch();
|
||||
await PageObjects.discover.saveSearch(`Ecommerce Data`); // rename the search back for the next test
|
||||
});
|
||||
|
||||
const url = await PageObjects.reporting.getReportURL(60000);
|
||||
const res = await PageObjects.reporting.getResponse(url);
|
||||
it('generates a report with discover:searchFieldsFromSource = true', async () => {
|
||||
await setupPage();
|
||||
await PageObjects.discover.loadSavedSearch('Ecommerce Data');
|
||||
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.get('content-type')).to.equal('text/csv; charset=utf-8');
|
||||
expectSnapshot(res.text).toMatch();
|
||||
await setFieldsFromSource(true);
|
||||
await browser.refresh();
|
||||
|
||||
const { text } = await getReport();
|
||||
expectSnapshot(text).toMatch();
|
||||
await setFieldsFromSource(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -5,267 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const CSV_RESULT_TIMEBASED_UTC = `"@timestamp",clientip,extension
|
||||
"Sep 20, 2015 @ 10:26:48.725","74.214.76.90",jpg
|
||||
"Sep 20, 2015 @ 10:26:48.540","146.86.123.109",jpg
|
||||
"Sep 20, 2015 @ 10:26:48.353","233.126.159.144",jpg
|
||||
"Sep 20, 2015 @ 10:26:45.468","153.139.156.196",png
|
||||
"Sep 20, 2015 @ 10:26:34.063","25.140.171.133",css
|
||||
"Sep 20, 2015 @ 10:26:11.181","239.249.202.59",jpg
|
||||
"Sep 20, 2015 @ 10:26:00.639","95.59.225.31",css
|
||||
"Sep 20, 2015 @ 10:26:00.094","247.174.57.245",jpg
|
||||
"Sep 20, 2015 @ 10:25:55.744","116.126.47.226",css
|
||||
"Sep 20, 2015 @ 10:25:54.701","169.228.188.120",jpg
|
||||
"Sep 20, 2015 @ 10:25:52.360","74.224.77.232",css
|
||||
"Sep 20, 2015 @ 10:25:49.913","97.83.96.39",css
|
||||
"Sep 20, 2015 @ 10:25:44.979","175.188.44.145",css
|
||||
"Sep 20, 2015 @ 10:25:40.968","89.143.125.181",jpg
|
||||
"Sep 20, 2015 @ 10:25:36.331","231.169.195.137",css
|
||||
"Sep 20, 2015 @ 10:25:34.064","137.205.146.206",jpg
|
||||
"Sep 20, 2015 @ 10:25:32.312","53.0.188.251",jpg
|
||||
"Sep 20, 2015 @ 10:25:27.254","111.214.104.239",jpg
|
||||
"Sep 20, 2015 @ 10:25:22.561","111.46.85.146",jpg
|
||||
"Sep 20, 2015 @ 10:25:06.674","55.100.60.111",jpg
|
||||
"Sep 20, 2015 @ 10:25:05.114","34.197.178.155",jpg
|
||||
"Sep 20, 2015 @ 10:24:55.114","163.123.136.118",jpg
|
||||
"Sep 20, 2015 @ 10:24:54.818","11.195.163.57",jpg
|
||||
"Sep 20, 2015 @ 10:24:53.742","96.222.137.213",png
|
||||
"Sep 20, 2015 @ 10:24:48.798","227.228.214.218",jpg
|
||||
"Sep 20, 2015 @ 10:24:20.223","228.53.110.116",jpg
|
||||
"Sep 20, 2015 @ 10:24:01.794","196.131.253.111",png
|
||||
"Sep 20, 2015 @ 10:23:49.521","125.163.133.47",jpg
|
||||
"Sep 20, 2015 @ 10:23:45.816","148.47.216.255",jpg
|
||||
"Sep 20, 2015 @ 10:23:36.052","51.105.100.214",jpg
|
||||
"Sep 20, 2015 @ 10:23:34.323","41.210.252.157",gif
|
||||
"Sep 20, 2015 @ 10:23:27.213","248.163.75.193",png
|
||||
"Sep 20, 2015 @ 10:23:14.866","48.43.210.167",png
|
||||
"Sep 20, 2015 @ 10:23:10.578","33.95.78.209",css
|
||||
"Sep 20, 2015 @ 10:23:07.001","96.40.73.208",css
|
||||
"Sep 20, 2015 @ 10:23:02.876","174.32.230.63",jpg
|
||||
"Sep 20, 2015 @ 10:23:00.019","140.233.207.177",jpg
|
||||
"Sep 20, 2015 @ 10:22:47.447","37.127.124.65",jpg
|
||||
"Sep 20, 2015 @ 10:22:45.803","130.171.208.139",png
|
||||
"Sep 20, 2015 @ 10:22:45.590","39.250.210.253",jpg
|
||||
"Sep 20, 2015 @ 10:22:43.997","248.239.221.43",css
|
||||
"Sep 20, 2015 @ 10:22:36.107","232.64.207.109",gif
|
||||
"Sep 20, 2015 @ 10:22:30.527","24.186.122.118",jpg
|
||||
"Sep 20, 2015 @ 10:22:25.697","23.3.174.206",jpg
|
||||
"Sep 20, 2015 @ 10:22:08.272","185.170.80.142",php
|
||||
"Sep 20, 2015 @ 10:21:40.822","202.22.74.232",png
|
||||
"Sep 20, 2015 @ 10:21:36.210","39.227.27.167",jpg
|
||||
"Sep 20, 2015 @ 10:21:19.154","140.233.207.177",jpg
|
||||
"Sep 20, 2015 @ 10:21:09.852","22.151.97.227",jpg
|
||||
"Sep 20, 2015 @ 10:21:06.079","157.39.25.197",css
|
||||
"Sep 20, 2015 @ 10:21:01.357","37.127.124.65",jpg
|
||||
"Sep 20, 2015 @ 10:20:56.519","23.184.94.58",jpg
|
||||
"Sep 20, 2015 @ 10:20:40.189","80.83.92.252",jpg
|
||||
"Sep 20, 2015 @ 10:20:27.012","66.194.157.171",png
|
||||
"Sep 20, 2015 @ 10:20:24.450","15.191.218.38",jpg
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_TIMEBASED_CUSTOM = `"@timestamp",clientip,extension
|
||||
"Sep 20, 2015 @ 03:26:48.725","74.214.76.90",jpg
|
||||
"Sep 20, 2015 @ 03:26:48.540","146.86.123.109",jpg
|
||||
"Sep 20, 2015 @ 03:26:48.353","233.126.159.144",jpg
|
||||
"Sep 20, 2015 @ 03:26:45.468","153.139.156.196",png
|
||||
"Sep 20, 2015 @ 03:26:34.063","25.140.171.133",css
|
||||
"Sep 20, 2015 @ 03:26:11.181","239.249.202.59",jpg
|
||||
"Sep 20, 2015 @ 03:26:00.639","95.59.225.31",css
|
||||
"Sep 20, 2015 @ 03:26:00.094","247.174.57.245",jpg
|
||||
"Sep 20, 2015 @ 03:25:55.744","116.126.47.226",css
|
||||
"Sep 20, 2015 @ 03:25:54.701","169.228.188.120",jpg
|
||||
"Sep 20, 2015 @ 03:25:52.360","74.224.77.232",css
|
||||
"Sep 20, 2015 @ 03:25:49.913","97.83.96.39",css
|
||||
"Sep 20, 2015 @ 03:25:44.979","175.188.44.145",css
|
||||
"Sep 20, 2015 @ 03:25:40.968","89.143.125.181",jpg
|
||||
"Sep 20, 2015 @ 03:25:36.331","231.169.195.137",css
|
||||
"Sep 20, 2015 @ 03:25:34.064","137.205.146.206",jpg
|
||||
"Sep 20, 2015 @ 03:25:32.312","53.0.188.251",jpg
|
||||
"Sep 20, 2015 @ 03:25:27.254","111.214.104.239",jpg
|
||||
"Sep 20, 2015 @ 03:25:22.561","111.46.85.146",jpg
|
||||
"Sep 20, 2015 @ 03:25:06.674","55.100.60.111",jpg
|
||||
"Sep 20, 2015 @ 03:25:05.114","34.197.178.155",jpg
|
||||
"Sep 20, 2015 @ 03:24:55.114","163.123.136.118",jpg
|
||||
"Sep 20, 2015 @ 03:24:54.818","11.195.163.57",jpg
|
||||
"Sep 20, 2015 @ 03:24:53.742","96.222.137.213",png
|
||||
"Sep 20, 2015 @ 03:24:48.798","227.228.214.218",jpg
|
||||
"Sep 20, 2015 @ 03:24:20.223","228.53.110.116",jpg
|
||||
"Sep 20, 2015 @ 03:24:01.794","196.131.253.111",png
|
||||
"Sep 20, 2015 @ 03:23:49.521","125.163.133.47",jpg
|
||||
"Sep 20, 2015 @ 03:23:45.816","148.47.216.255",jpg
|
||||
"Sep 20, 2015 @ 03:23:36.052","51.105.100.214",jpg
|
||||
"Sep 20, 2015 @ 03:23:34.323","41.210.252.157",gif
|
||||
"Sep 20, 2015 @ 03:23:27.213","248.163.75.193",png
|
||||
"Sep 20, 2015 @ 03:23:14.866","48.43.210.167",png
|
||||
"Sep 20, 2015 @ 03:23:10.578","33.95.78.209",css
|
||||
"Sep 20, 2015 @ 03:23:07.001","96.40.73.208",css
|
||||
"Sep 20, 2015 @ 03:23:02.876","174.32.230.63",jpg
|
||||
"Sep 20, 2015 @ 03:23:00.019","140.233.207.177",jpg
|
||||
"Sep 20, 2015 @ 03:22:47.447","37.127.124.65",jpg
|
||||
"Sep 20, 2015 @ 03:22:45.803","130.171.208.139",png
|
||||
"Sep 20, 2015 @ 03:22:45.590","39.250.210.253",jpg
|
||||
"Sep 20, 2015 @ 03:22:43.997","248.239.221.43",css
|
||||
"Sep 20, 2015 @ 03:22:36.107","232.64.207.109",gif
|
||||
"Sep 20, 2015 @ 03:22:30.527","24.186.122.118",jpg
|
||||
"Sep 20, 2015 @ 03:22:25.697","23.3.174.206",jpg
|
||||
"Sep 20, 2015 @ 03:22:08.272","185.170.80.142",php
|
||||
"Sep 20, 2015 @ 03:21:40.822","202.22.74.232",png
|
||||
"Sep 20, 2015 @ 03:21:36.210","39.227.27.167",jpg
|
||||
"Sep 20, 2015 @ 03:21:19.154","140.233.207.177",jpg
|
||||
"Sep 20, 2015 @ 03:21:09.852","22.151.97.227",jpg
|
||||
"Sep 20, 2015 @ 03:21:06.079","157.39.25.197",css
|
||||
"Sep 20, 2015 @ 03:21:01.357","37.127.124.65",jpg
|
||||
"Sep 20, 2015 @ 03:20:56.519","23.184.94.58",jpg
|
||||
"Sep 20, 2015 @ 03:20:40.189","80.83.92.252",jpg
|
||||
"Sep 20, 2015 @ 03:20:27.012","66.194.157.171",png
|
||||
"Sep 20, 2015 @ 03:20:24.450","15.191.218.38",jpg
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_TIMELESS = `name,power
|
||||
"Jonelle-Jane Marth","1.177"
|
||||
"Suzie-May Rishel","1.824"
|
||||
"Suzie-May Rishel","2.077"
|
||||
"Rosana Casto","2.808"
|
||||
"Stephen Cortez","4.986"
|
||||
"Jonelle-Jane Marth","6.156"
|
||||
"Jonelle-Jane Marth","7.097"
|
||||
"Florinda Alejandro","10.373"
|
||||
"Jonelle-Jane Marth","14.807"
|
||||
"Suzie-May Rishel","19.738"
|
||||
"Suzie-May Rishel","20.92"
|
||||
"Florinda Alejandro","22.209"
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_SCRIPTED = `date,name,percent,value,year,"years_ago",gender
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fecki,0,92,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fecki,0,78,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fecky,"0.001","2,071","1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fekki,0,6,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felen,0,40,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felia,0,21,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felina,0,6,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felinda,"0.001","1,620","1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felinda,"0.001","1,886","1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felisa,0,5,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felita,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felkys,0,7,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felkys,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fell,0,6,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felle,0,22,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felma,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felynda,0,31,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fenita,0,219,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fenjamin,0,22,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fenjamin,0,27,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fenji,0,5,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Fennie,0,16,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fenny,0,5,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Ferenice,0,9,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Frijida,0,5,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Frita,0,14,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fritney,0,10,"1,980","39.00000000000000000000",F
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_SCRIPTED_REQUERY = `date,name,percent,value,year,"years_ago",gender
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felen,0,40,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felia,0,21,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felina,0,6,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felinda,"0.001","1,620","1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felinda,"0.001","1,886","1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felisa,0,5,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felita,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felkys,0,7,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felkys,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Fell,0,6,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felle,0,22,"1,980","39.00000000000000000000",F
|
||||
"Jan 1, 1981 @ 00:00:00.000",Felma,0,8,"1,981","38.00000000000000000000",F
|
||||
"Jan 1, 1980 @ 00:00:00.000",Felynda,0,31,"1,980","39.00000000000000000000",F
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_SCRIPTED_RESORTED = `date,year,name,value,"years_ago"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Farbara,"6,456","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Farbara,"8,026","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fecky,"1,930","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Fecky,"2,071","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Felinda,"1,886","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Feth,"3,685","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Feth,"4,246","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fetty,"1,763","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Fetty,"1,967","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Feverly,"1,987","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Feverly,"2,249","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fonnie,"2,330","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Fonnie,"2,748","39.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Frenda,"7,162","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Frenda,"8,335","39.00000000000000000000"
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_HUGE = `date,year,name,value,"years_ago"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Fobby,"2,791","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Frent,"3,416","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Frett,"2,679","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Filly,"3,366","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Frian,"34,468","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Fenjamin,"7,191","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Frandon,"5,863","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Fruce,"1,855","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Fryan,"7,236","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Frad,"2,482","35.00000000000000000000"
|
||||
"Jan 1, 1984 @ 00:00:00.000","1,984",Fradley,"5,175","35.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Fryan,"7,114","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Fradley,"4,752","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Frian,"35,717","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Farbara,"4,434","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Fenjamin,"5,235","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Fruce,"1,914","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Fobby,"2,888","36.00000000000000000000"
|
||||
"Jan 1, 1983 @ 00:00:00.000","1,983",Frett,"3,031","36.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Fonnie,"1,853","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Frandy,"2,082","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Fecky,"1,786","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Frandi,"2,056","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Fridget,"1,864","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Farbara,"5,081","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Feth,"2,818","37.00000000000000000000"
|
||||
"Jan 1, 1982 @ 00:00:00.000","1,982",Frenda,"6,270","37.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fetty,"1,763","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fonnie,"2,330","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Farbara,"6,456","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Felinda,"1,886","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Frenda,"7,162","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Feth,"3,685","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Feverly,"1,987","38.00000000000000000000"
|
||||
"Jan 1, 1981 @ 00:00:00.000","1,981",Fecky,"1,930","38.00000000000000000000"
|
||||
"Jan 1, 1980 @ 00:00:00.000","1,980",Fonnie,"2,748","39.00000000000000000000"
|
||||
`;
|
||||
|
||||
// 'UTC'
|
||||
export const CSV_RESULT_NANOS = `date,message,"_id"
|
||||
"Jan 1, 2015 @ 12:10:30.123456789","Hello 2",
|
||||
"Jan 1, 2015 @ 12:10:30.000000000","Hello 1",
|
||||
`;
|
||||
|
||||
// 'America/New_York'
|
||||
export const CSV_RESULT_NANOS_CUSTOM = `date,message,"_id"
|
||||
"Jan 1, 2015 @ 07:10:30.123456789","Hello 2",
|
||||
"Jan 1, 2015 @ 07:10:30.000000000","Hello 1",
|
||||
`;
|
||||
|
||||
export const CSV_RESULT_DOCVALUE = `"order_date",category,currency,"customer_id","order_id","day_of_week_i","order_date","products.created_on",sku
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,26,569309,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0364103641"",""ZO0708807088""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,24,569311,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0024600246"",""ZO0660706607""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing"",""Men's Shoes""]",EUR,31,569312,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0425104251"",""ZO0107901079""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Men's Shoes""]",EUR,14,569336,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0512505125"",""ZO0384103841""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing""]",EUR,28,569337,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0634106341"",""ZO0066900669""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Men's Accessories"",""Men's Clothing""]",EUR,31,569338,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0702507025"",""ZO0528105281""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Shoes"",""Women's Clothing""]",EUR,27,569356,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0010500105"",""ZO0172201722""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing"",""Men's Shoes""]",EUR,19,569362,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0292402924"",""ZO0681006810""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Accessories"",""Women's Clothing""]",EUR,42,569370,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0358603586"",""ZO0641106411""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Accessories""]",EUR,20,569371,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0225702257"",""ZO0186601866""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Women's Clothing"",""Women's Shoes""]",EUR,43,569375,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0347603476"",""ZO0668806688""]"
|
||||
"Jun 26, 2019 @ 00:00:00.000","[""Men's Clothing""]",EUR,48,569387,3,"Jun 26, 2019 @ 00:00:00.000","[""Dec 15, 2016 @ 00:00:00.000"",""Dec 15, 2016 @ 00:00:00.000""]","[""ZO0593805938"",""ZO0125201252""]"
|
||||
`;
|
||||
|
||||
// This concatenates lines of multi-line string into a single line.
|
||||
// It is so long strings can be entered at short widths, making syntax highlighting easier on editors
|
||||
function singleLine(literals: TemplateStringsArray): string {
|
||||
|
|
|
@ -42,7 +42,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
`--server.maxPayloadBytes=1679958`,
|
||||
`--server.port=${kbnTestConfig.getPort()}`,
|
||||
`--xpack.reporting.capture.maxAttempts=1`,
|
||||
`--xpack.reporting.csv.maxSizeBytes=2850`,
|
||||
`--xpack.reporting.csv.maxSizeBytes=6000`,
|
||||
`--xpack.reporting.queue.pollInterval=3000`,
|
||||
`--xpack.security.session.idleTimeout=3600000`,
|
||||
`--xpack.reporting.capture.networkPolicy.rules=${JSON.stringify(testPolicyRules)}`,
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource Exports CSV with all fields when using defaults 1`] = `
|
||||
"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,\\"category.keyword\\",currency,\\"customer_first_name\\",\\"customer_first_name.keyword\\",\\"customer_full_name\\",\\"customer_full_name.keyword\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_last_name.keyword\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,\\"geoip.city_name\\",\\"geoip.continent_name\\",\\"geoip.country_iso_code\\",\\"geoip.location\\",\\"geoip.region_name\\",manufacturer,\\"manufacturer.keyword\\",\\"order_date\\",\\"order_id\\",\\"products._id\\",\\"products._id.keyword\\",\\"products.base_price\\",\\"products.base_unit_price\\",\\"products.category\\",\\"products.category.keyword\\",\\"products.created_on\\",\\"products.discount_amount\\",\\"products.discount_percentage\\",\\"products.manufacturer\\",\\"products.manufacturer.keyword\\",\\"products.min_price\\",\\"products.price\\",\\"products.product_id\\",\\"products.product_name\\",\\"products.product_name.keyword\\",\\"products.quantity\\",\\"products.sku\\",\\"products.tax_amount\\",\\"products.taxful_price\\",\\"products.taxless_price\\",\\"products.unit_discount_amount\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user
|
||||
3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al\\",\\"Sultan Al Boone\\",\\"Sultan Al Boone\\",MALE,19,Boone,Boone,,Saturday,5,\\"sultan al@boone-family.zzz\\",\\"Abu Dhabi\\",Asia,AE,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
54.4,
|
||||
24.5
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"Abu Dhabi\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"sold_product_716724_23975, sold_product_716724_6338, sold_product_716724_14116, sold_product_716724_15290\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0, 0, 0\\",\\"0, 0, 0, 0\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"Angeldale, Oceanavigations, Microlutions, Oceanavigations\\",\\"42.39, 32.99, 10.34, 6.11\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"23,975, 6,338, 14,116, 15,290\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"Winter boots - cognac, Trenchcoat - black, Watch - black, Hat - light grey multicolor\\",\\"1, 1, 1, 1\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"0, 0, 0, 0\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"79.99, 59.99, 21.99, 11.99\\",\\"0, 0, 0, 0\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",\\"173.96\\",\\"173.96\\",4,4,order,sultan
|
||||
9gMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",EUR,Pia,Pia,\\"Pia Richards\\",\\"Pia Richards\\",FEMALE,45,Richards,Richards,,Saturday,5,\\"pia@richards-family.zzz\\",Cannes,Europe,FR,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
7,
|
||||
43.6
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"Alpes-Maritimes\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"sold_product_591503_14761, sold_product_591503_11632\\",\\"20.99, 20.99\\",\\"20.99, 20.99\\",\\"Women's Shoes, Women's Clothing\\",\\"Women's Shoes, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Tigress Enterprises, Pyramidustries\\",\\"10.7, 9.87\\",\\"20.99, 20.99\\",\\"14,761, 11,632\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"Classic heels - blue, Summer dress - coral/pink\\",\\"1, 1\\",\\"ZO0006400064, ZO0150601506\\",\\"0, 0\\",\\"20.99, 20.99\\",\\"20.99, 20.99\\",\\"0, 0\\",\\"ZO0006400064, ZO0150601506\\",\\"41.98\\",\\"41.98\\",2,2,order,pia
|
||||
BgMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Clothing\\",\\"Women's Clothing\\",EUR,Brigitte,Brigitte,\\"Brigitte Meyer\\",\\"Brigitte Meyer\\",FEMALE,12,Meyer,Meyer,,Saturday,5,\\"brigitte@meyer-family.zzz\\",\\"New York\\",\\"North America\\",US,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
-74,
|
||||
40.8
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"New York\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"sold_product_591709_20734, sold_product_591709_7539\\",\\"7.99, 32.99\\",\\"7.99, 32.99\\",\\"Women's Clothing, Women's Clothing\\",\\"Women's Clothing, Women's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Spherecords, Tigress Enterprises\\",\\"Spherecords, Tigress Enterprises\\",\\"3.6, 17.48\\",\\"7.99, 32.99\\",\\"20,734, 7,539\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"Basic T-shirt - dark blue, Summer dress - scarab\\",\\"1, 1\\",\\"ZO0638206382, ZO0038800388\\",\\"0, 0\\",\\"7.99, 32.99\\",\\"7.99, 32.99\\",\\"0, 0\\",\\"ZO0638206382, ZO0038800388\\",\\"40.98\\",\\"40.98\\",2,2,order,brigitte
|
||||
KQMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Clothing\\",\\"Men's Clothing\\",EUR,Abd,Abd,\\"Abd Mccarthy\\",\\"Abd Mccarthy\\",MALE,52,Mccarthy,Mccarthy,,Saturday,5,\\"abd@mccarthy-family.zzz\\",Cairo,Africa,EG,\\"{
|
||||
\\"\\"coordinates\\"\\": [
|
||||
31.3,
|
||||
30.1
|
||||
],
|
||||
\\"\\"type\\"\\": \\"\\"Point\\"\\"
|
||||
}\\",\\"Cairo Governorate\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"Jul 12, 2019 @ 00:00:00.000\\",590937,\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"sold_product_590937_14438, sold_product_590937_23607\\",\\"28.99, 12.99\\",\\"28.99, 12.99\\",\\"Men's Clothing, Men's Clothing\\",\\"Men's Clothing, Men's Clothing\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"0, 0\\",\\"0, 0\\",\\"Oceanavigations, Elitelligence\\",\\"Oceanavigations, Elitelligence\\",\\"13.34, 6.11\\",\\"28.99, 12.99\\",\\"14,438, 23,607\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"Jumper - dark grey multicolor, Print T-shirt - black\\",\\"1, 1\\",\\"ZO0297602976, ZO0565605656\\",\\"0, 0\\",\\"28.99, 12.99\\",\\"28.99, 12.99\\",\\"0, 0\\",\\"ZO0297602976, ZO0565605656\\",\\"41.98\\",\\"41.98\\",2,2,order,abd
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource Exports CSV with almost all fields when using fieldsFromSource 1`] = `
|
||||
"\\"_id\\",\\"_index\\",\\"_score\\",\\"_type\\",category,currency,\\"customer_first_name\\",\\"customer_full_name\\",\\"customer_gender\\",\\"customer_id\\",\\"customer_last_name\\",\\"customer_phone\\",\\"day_of_week\\",\\"day_of_week_i\\",email,geoip,manufacturer,\\"order_date\\",\\"order_id\\",products,\\"products.created_on\\",sku,\\"taxful_total_price\\",\\"taxless_total_price\\",\\"total_quantity\\",\\"total_unique_products\\",type,user
|
||||
3AMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,\\"Sultan Al\\",\\"Sultan Al Boone\\",MALE,19,Boone,\\"-\\",Saturday,5,\\"sultan al@boone-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"Abu Dhabi\\"\\",\\"\\"continent_name\\"\\":\\"\\"Asia\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"AE\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":24.5,\\"\\"lon\\"\\":54.4},\\"\\"region_name\\"\\":\\"\\"Abu Dhabi\\"\\"}\\",\\"Angeldale, Oceanavigations, Microlutions\\",\\"Jul 12, 2019 @ 00:00:00.000\\",716724,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_716724_23975\\"\\",\\"\\"base_price\\"\\":79.99,\\"\\"base_unit_price\\"\\":79.99,\\"\\"category\\"\\":\\"\\"Men's Shoes\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Angeldale\\"\\",\\"\\"min_price\\"\\":42.39,\\"\\"price\\"\\":79.99,\\"\\"product_id\\"\\":23975,\\"\\"product_name\\"\\":\\"\\"Winter boots - cognac\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0687606876\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":79.99,\\"\\"taxless_price\\"\\":79.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_6338\\"\\",\\"\\"base_price\\"\\":59.99,\\"\\"base_unit_price\\"\\":59.99,\\"\\"category\\"\\":\\"\\"Men's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Oceanavigations\\"\\",\\"\\"min_price\\"\\":32.99,\\"\\"price\\"\\":59.99,\\"\\"product_id\\"\\":6338,\\"\\"product_name\\"\\":\\"\\"Trenchcoat - black\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0290502905\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":59.99,\\"\\"taxless_price\\"\\":59.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_14116\\"\\",\\"\\"base_price\\"\\":21.99,\\"\\"base_unit_price\\"\\":21.99,\\"\\"category\\"\\":\\"\\"Women's Accessories\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Microlutions\\"\\",\\"\\"min_price\\"\\":10.34,\\"\\"price\\"\\":21.99,\\"\\"product_id\\"\\":14116,\\"\\"product_name\\"\\":\\"\\"Watch - black\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0126701267\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":21.99,\\"\\"taxless_price\\"\\":21.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_716724_15290\\"\\",\\"\\"base_price\\"\\":11.99,\\"\\"base_unit_price\\"\\":11.99,\\"\\"category\\"\\":\\"\\"Men's Accessories\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Oceanavigations\\"\\",\\"\\"min_price\\"\\":6.11,\\"\\"price\\"\\":11.99,\\"\\"product_id\\"\\":15290,\\"\\"product_name\\"\\":\\"\\"Hat - light grey multicolor\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0308503085\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":11.99,\\"\\"taxless_price\\"\\":11.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\",173.96,173.96,4,4,order,sultan
|
||||
9gMtOW0BH63Xcmy432DJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Shoes, Women's Clothing\\",EUR,Pia,\\"Pia Richards\\",FEMALE,45,Richards,\\"-\\",Saturday,5,\\"pia@richards-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"Cannes\\"\\",\\"\\"continent_name\\"\\":\\"\\"Europe\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"FR\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":43.6,\\"\\"lon\\"\\":7},\\"\\"region_name\\"\\":\\"\\"Alpes-Maritimes\\"\\"}\\",\\"Tigress Enterprises, Pyramidustries\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591503,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_591503_14761\\"\\",\\"\\"base_price\\"\\":20.99,\\"\\"base_unit_price\\"\\":20.99,\\"\\"category\\"\\":\\"\\"Women's Shoes\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Tigress Enterprises\\"\\",\\"\\"min_price\\"\\":10.7,\\"\\"price\\"\\":20.99,\\"\\"product_id\\"\\":14761,\\"\\"product_name\\"\\":\\"\\"Classic heels - blue\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0006400064\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":20.99,\\"\\"taxless_price\\"\\":20.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_591503_11632\\"\\",\\"\\"base_price\\"\\":20.99,\\"\\"base_unit_price\\"\\":20.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Pyramidustries\\"\\",\\"\\"min_price\\"\\":9.87,\\"\\"price\\"\\":20.99,\\"\\"product_id\\"\\":11632,\\"\\"product_name\\"\\":\\"\\"Summer dress - coral/pink\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0150601506\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":20.99,\\"\\"taxless_price\\"\\":20.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\",41.98,41.98,2,2,order,pia
|
||||
BgMtOW0BH63Xcmy432LJ,ecommerce,\\"-\\",\\"-\\",\\"Women's Clothing\\",EUR,Brigitte,\\"Brigitte Meyer\\",FEMALE,12,Meyer,\\"-\\",Saturday,5,\\"brigitte@meyer-family.zzz\\",\\"{\\"\\"city_name\\"\\":\\"\\"New York\\"\\",\\"\\"continent_name\\"\\":\\"\\"North America\\"\\",\\"\\"country_iso_code\\"\\":\\"\\"US\\"\\",\\"\\"location\\"\\":{\\"\\"lat\\"\\":40.8,\\"\\"lon\\"\\":-74},\\"\\"region_name\\"\\":\\"\\"New York\\"\\"}\\",\\"Spherecords, Tigress Enterprises\\",\\"Jul 12, 2019 @ 00:00:00.000\\",591709,\\"{\\"\\"_id\\"\\":\\"\\"sold_product_591709_20734\\"\\",\\"\\"base_price\\"\\":7.99,\\"\\"base_unit_price\\"\\":7.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Spherecords\\"\\",\\"\\"min_price\\"\\":3.6,\\"\\"price\\"\\":7.99,\\"\\"product_id\\"\\":20734,\\"\\"product_name\\"\\":\\"\\"Basic T-shirt - dark blue\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0638206382\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":7.99,\\"\\"taxless_price\\"\\":7.99,\\"\\"unit_discount_amount\\"\\":0}, {\\"\\"_id\\"\\":\\"\\"sold_product_591709_7539\\"\\",\\"\\"base_price\\"\\":32.99,\\"\\"base_unit_price\\"\\":32.99,\\"\\"category\\"\\":\\"\\"Women's Clothing\\"\\",\\"\\"created_on\\"\\":\\"\\"2016-12-31T00:00:00+00:00\\"\\",\\"\\"discount_amount\\"\\":0,\\"\\"discount_percentage\\"\\":0,\\"\\"manufacturer\\"\\":\\"\\"Tigress Enterprises\\"\\",\\"\\"min_price\\"\\":17.48,\\"\\"price\\"\\":32.99,\\"\\"product_id\\"\\":7539,\\"\\"product_name\\"\\":\\"\\"Summer dress - scarab\\"\\",\\"\\"quantity\\"\\":1,\\"\\"sku\\"\\":\\"\\"ZO0038800388\\"\\",\\"\\"tax_amount\\"\\":0,\\"\\"taxful_price\\"\\":32.99,\\"\\"taxless_price\\"\\":32.99,\\"\\"unit_discount_amount\\"\\":0}\\",\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\",40.98,40.98,2,2,order,brigitte
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource Logs the error explanation if the search query returns an error 1`] = `"{\\"statusCode\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"An internal server error occurred.\\"}"`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource date formatting Formatted date_nanos data, UTC timezone 1`] = `
|
||||
"date,message
|
||||
\\"Jan 1, 2015 @ 12:10:30.123456789\\",\\"Hello 2\\"
|
||||
\\"Jan 1, 2015 @ 12:10:30.000000000\\",\\"Hello 1\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource date formatting Formatted date_nanos data, custom timezone (New York) 1`] = `
|
||||
"date,message
|
||||
\\"Jan 1, 2015 @ 07:10:30.123456789\\",\\"Hello 2\\"
|
||||
\\"Jan 1, 2015 @ 07:10:30.000000000\\",\\"Hello 1\\"
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource date formatting With filters and timebased data, default to UTC 1`] = `
|
||||
"\\"@timestamp\\",clientip,extension
|
||||
\\"Sep 20, 2015 @ 10:26:48.725\\",\\"74.214.76.90\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:26:48.540\\",\\"146.86.123.109\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:26:48.353\\",\\"233.126.159.144\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:26:45.468\\",\\"153.139.156.196\\",png
|
||||
\\"Sep 20, 2015 @ 10:26:34.063\\",\\"25.140.171.133\\",css
|
||||
\\"Sep 20, 2015 @ 10:26:11.181\\",\\"239.249.202.59\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:26:00.639\\",\\"95.59.225.31\\",css
|
||||
\\"Sep 20, 2015 @ 10:26:00.094\\",\\"247.174.57.245\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:55.744\\",\\"116.126.47.226\\",css
|
||||
\\"Sep 20, 2015 @ 10:25:54.701\\",\\"169.228.188.120\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:52.360\\",\\"74.224.77.232\\",css
|
||||
\\"Sep 20, 2015 @ 10:25:49.913\\",\\"97.83.96.39\\",css
|
||||
\\"Sep 20, 2015 @ 10:25:44.979\\",\\"175.188.44.145\\",css
|
||||
\\"Sep 20, 2015 @ 10:25:40.968\\",\\"89.143.125.181\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:36.331\\",\\"231.169.195.137\\",css
|
||||
\\"Sep 20, 2015 @ 10:25:34.064\\",\\"137.205.146.206\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:32.312\\",\\"53.0.188.251\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:27.254\\",\\"111.214.104.239\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:22.561\\",\\"111.46.85.146\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:06.674\\",\\"55.100.60.111\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:25:05.114\\",\\"34.197.178.155\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:24:55.114\\",\\"163.123.136.118\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:24:54.818\\",\\"11.195.163.57\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:24:53.742\\",\\"96.222.137.213\\",png
|
||||
\\"Sep 20, 2015 @ 10:24:48.798\\",\\"227.228.214.218\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:24:20.223\\",\\"228.53.110.116\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:24:01.794\\",\\"196.131.253.111\\",png
|
||||
\\"Sep 20, 2015 @ 10:23:49.521\\",\\"125.163.133.47\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:23:45.816\\",\\"148.47.216.255\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:23:36.052\\",\\"51.105.100.214\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:23:34.323\\",\\"41.210.252.157\\",gif
|
||||
\\"Sep 20, 2015 @ 10:23:27.213\\",\\"248.163.75.193\\",png
|
||||
\\"Sep 20, 2015 @ 10:23:14.866\\",\\"48.43.210.167\\",png
|
||||
\\"Sep 20, 2015 @ 10:23:10.578\\",\\"33.95.78.209\\",css
|
||||
\\"Sep 20, 2015 @ 10:23:07.001\\",\\"96.40.73.208\\",css
|
||||
\\"Sep 20, 2015 @ 10:23:02.876\\",\\"174.32.230.63\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:23:00.019\\",\\"140.233.207.177\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:22:47.447\\",\\"37.127.124.65\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:22:45.803\\",\\"130.171.208.139\\",png
|
||||
\\"Sep 20, 2015 @ 10:22:45.590\\",\\"39.250.210.253\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:22:43.997\\",\\"248.239.221.43\\",css
|
||||
\\"Sep 20, 2015 @ 10:22:36.107\\",\\"232.64.207.109\\",gif
|
||||
\\"Sep 20, 2015 @ 10:22:30.527\\",\\"24.186.122.118\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:22:25.697\\",\\"23.3.174.206\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:22:08.272\\",\\"185.170.80.142\\",php
|
||||
\\"Sep 20, 2015 @ 10:21:40.822\\",\\"202.22.74.232\\",png
|
||||
\\"Sep 20, 2015 @ 10:21:36.210\\",\\"39.227.27.167\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:21:19.154\\",\\"140.233.207.177\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:21:09.852\\",\\"22.151.97.227\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:21:06.079\\",\\"157.39.25.197\\",css
|
||||
\\"Sep 20, 2015 @ 10:21:01.357\\",\\"37.127.124.65\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:20:56.519\\",\\"23.184.94.58\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:20:40.189\\",\\"80.83.92.252\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:20:27.012\\",\\"66.194.157.171\\",png
|
||||
\\"Sep 20, 2015 @ 10:20:24.450\\",\\"15.191.218.38\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:19:45.764\\",\\"199.113.69.162\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:19:43.754\\",\\"171.243.18.67\\",gif
|
||||
\\"Sep 20, 2015 @ 10:19:41.208\\",\\"126.87.234.213\\",jpg
|
||||
\\"Sep 20, 2015 @ 10:19:40.307\\",\\"78.216.173.242\\",css
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource date formatting With filters and timebased data, non-default timezone 1`] = `
|
||||
"\\"@timestamp\\",clientip,extension
|
||||
\\"Sep 20, 2015 @ 03:26:48.725\\",\\"74.214.76.90\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:26:48.540\\",\\"146.86.123.109\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:26:48.353\\",\\"233.126.159.144\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:26:45.468\\",\\"153.139.156.196\\",png
|
||||
\\"Sep 20, 2015 @ 03:26:34.063\\",\\"25.140.171.133\\",css
|
||||
\\"Sep 20, 2015 @ 03:26:11.181\\",\\"239.249.202.59\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:26:00.639\\",\\"95.59.225.31\\",css
|
||||
\\"Sep 20, 2015 @ 03:26:00.094\\",\\"247.174.57.245\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:55.744\\",\\"116.126.47.226\\",css
|
||||
\\"Sep 20, 2015 @ 03:25:54.701\\",\\"169.228.188.120\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:52.360\\",\\"74.224.77.232\\",css
|
||||
\\"Sep 20, 2015 @ 03:25:49.913\\",\\"97.83.96.39\\",css
|
||||
\\"Sep 20, 2015 @ 03:25:44.979\\",\\"175.188.44.145\\",css
|
||||
\\"Sep 20, 2015 @ 03:25:40.968\\",\\"89.143.125.181\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:36.331\\",\\"231.169.195.137\\",css
|
||||
\\"Sep 20, 2015 @ 03:25:34.064\\",\\"137.205.146.206\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:32.312\\",\\"53.0.188.251\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:27.254\\",\\"111.214.104.239\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:22.561\\",\\"111.46.85.146\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:06.674\\",\\"55.100.60.111\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:25:05.114\\",\\"34.197.178.155\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:24:55.114\\",\\"163.123.136.118\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:24:54.818\\",\\"11.195.163.57\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:24:53.742\\",\\"96.222.137.213\\",png
|
||||
\\"Sep 20, 2015 @ 03:24:48.798\\",\\"227.228.214.218\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:24:20.223\\",\\"228.53.110.116\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:24:01.794\\",\\"196.131.253.111\\",png
|
||||
\\"Sep 20, 2015 @ 03:23:49.521\\",\\"125.163.133.47\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:23:45.816\\",\\"148.47.216.255\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:23:36.052\\",\\"51.105.100.214\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:23:34.323\\",\\"41.210.252.157\\",gif
|
||||
\\"Sep 20, 2015 @ 03:23:27.213\\",\\"248.163.75.193\\",png
|
||||
\\"Sep 20, 2015 @ 03:23:14.866\\",\\"48.43.210.167\\",png
|
||||
\\"Sep 20, 2015 @ 03:23:10.578\\",\\"33.95.78.209\\",css
|
||||
\\"Sep 20, 2015 @ 03:23:07.001\\",\\"96.40.73.208\\",css
|
||||
\\"Sep 20, 2015 @ 03:23:02.876\\",\\"174.32.230.63\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:23:00.019\\",\\"140.233.207.177\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:22:47.447\\",\\"37.127.124.65\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:22:45.803\\",\\"130.171.208.139\\",png
|
||||
\\"Sep 20, 2015 @ 03:22:45.590\\",\\"39.250.210.253\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:22:43.997\\",\\"248.239.221.43\\",css
|
||||
\\"Sep 20, 2015 @ 03:22:36.107\\",\\"232.64.207.109\\",gif
|
||||
\\"Sep 20, 2015 @ 03:22:30.527\\",\\"24.186.122.118\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:22:25.697\\",\\"23.3.174.206\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:22:08.272\\",\\"185.170.80.142\\",php
|
||||
\\"Sep 20, 2015 @ 03:21:40.822\\",\\"202.22.74.232\\",png
|
||||
\\"Sep 20, 2015 @ 03:21:36.210\\",\\"39.227.27.167\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:21:19.154\\",\\"140.233.207.177\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:21:09.852\\",\\"22.151.97.227\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:21:06.079\\",\\"157.39.25.197\\",css
|
||||
\\"Sep 20, 2015 @ 03:21:01.357\\",\\"37.127.124.65\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:20:56.519\\",\\"23.184.94.58\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:20:40.189\\",\\"80.83.92.252\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:20:27.012\\",\\"66.194.157.171\\",png
|
||||
\\"Sep 20, 2015 @ 03:20:24.450\\",\\"15.191.218.38\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:19:45.764\\",\\"199.113.69.162\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:19:43.754\\",\\"171.243.18.67\\",gif
|
||||
\\"Sep 20, 2015 @ 03:19:41.208\\",\\"126.87.234.213\\",jpg
|
||||
\\"Sep 20, 2015 @ 03:19:40.307\\",\\"78.216.173.242\\",css
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource non-timebased Handle _id and _index columns 1`] = `
|
||||
"date,message,\\"_id\\",\\"_index\\"
|
||||
\\"Jan 1, 2015 @ 12:10:30.123456789\\",\\"Hello 2\\",2,nanos
|
||||
\\"Jan 1, 2015 @ 12:10:30.000000000\\",\\"Hello 1\\",1,nanos
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource non-timebased With filters and non-timebased data 1`] = `
|
||||
"name,power
|
||||
\\"Jonelle-Jane Marth\\",1
|
||||
\\"Suzie-May Rishel\\",1
|
||||
\\"Suzie-May Rishel\\",2
|
||||
\\"Rosana Casto\\",2
|
||||
\\"Stephen Cortez\\",4
|
||||
\\"Jonelle-Jane Marth\\",6
|
||||
\\"Jonelle-Jane Marth\\",7
|
||||
\\"Florinda Alejandro\\",10
|
||||
\\"Jonelle-Jane Marth\\",14
|
||||
\\"Suzie-May Rishel\\",19
|
||||
\\"Suzie-May Rishel\\",20
|
||||
\\"Florinda Alejandro\\",22
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Reporting APIs CSV Generation from SearchSource validation Searches large amount of data, stops at Max Size Reached 1`] = `
|
||||
"\\"order_date\\",category,currency,\\"customer_id\\",\\"order_id\\",\\"day_of_week_i\\",\\"products.created_on\\",sku
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing, Women's Accessories, Men's Accessories\\",EUR,19,716724,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0687606876, ZO0290502905, ZO0126701267, ZO0308503085\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,45,591503,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0006400064, ZO0150601506\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591709,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638206382, ZO0038800388\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,52,590937,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0297602976, ZO0565605656\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,29,590976,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0561405614, ZO0281602816\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,41,591636,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0385003850, ZO0408604086\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes\\",EUR,30,591539,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0505605056, ZO0513605136\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,41,591598,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0276702767, ZO0291702917\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,44,590927,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0046600466, ZO0050800508\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,48,590970,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0455604556, ZO0680806808\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,46,591299,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0229002290, ZO0674406744\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,36,591133,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0529905299, ZO0617006170\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,13,591175,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0299402994, ZO0433504335\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,21,591297,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0257502575, ZO0451704517\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,14,591149,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0584905849, ZO0578405784\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,27,591754,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0335803358, ZO0325903259\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Shoes\\",EUR,42,591803,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0645906459, ZO0324303243\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,46,592082,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0034400344, ZO0492904929\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Accessories\\",EUR,27,591283,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0239302393, ZO0198501985\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,4,591148,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0290302903, ZO0513705137\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Accessories, Men's Clothing\\",EUR,51,591417,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0464504645, ZO0621006210\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,14,591562,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0544305443, ZO0108001080\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing, Women's Accessories\\",EUR,5,590996,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0638106381, ZO0096900969\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes\\",EUR,27,591317,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0366203662, ZO0139501395\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,38,591362,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0541805418, ZO0594105941\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Shoes, Men's Clothing\\",EUR,30,591411,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0693506935, ZO0532405324\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing, Men's Shoes\\",EUR,38,722629,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0424204242, ZO0403504035, ZO0506705067, ZO0395603956\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,16,591041,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0418704187, ZO0557105571\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,6,591074,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0268602686, ZO0484704847\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,7,591349,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0474804748, ZO0560705607\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Accessories, Women's Clothing\\",EUR,44,591374,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0206002060, ZO0268302683\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Clothing\\",EUR,12,591230,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0226902269, ZO0660106601\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes, Women's Clothing\\",EUR,17,591717,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0248002480, ZO0646706467\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Women's Shoes\\",EUR,42,591768,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0005800058, ZO0133901339\\"
|
||||
\\"Jul 12, 2019 @ 00:00:00.000\\",\\"Men's Clothing\\",EUR,21,591810,5,\\"Dec 31, 2016 @ 00:00:00.000, Dec 31, 2016 @ 00:00:00.000\\",\\"ZO0587405874, ZO0590305903\\"
|
||||
"
|
||||
`;
|
|
@ -1,411 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import supertest from 'supertest';
|
||||
import * as fixtures from '../fixtures';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
interface GenerateOpts {
|
||||
timerange?: {
|
||||
timezone: string;
|
||||
min?: number | string | Date;
|
||||
max?: number | string | Date;
|
||||
};
|
||||
state: any;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertestSvc = getService('supertest');
|
||||
const reportingAPI = getService('reportingAPI');
|
||||
|
||||
const generateAPI = {
|
||||
getCsvFromSavedSearch: async (
|
||||
id: string,
|
||||
{ timerange, state }: GenerateOpts,
|
||||
isImmediate = true
|
||||
) => {
|
||||
return await supertestSvc
|
||||
.post(`/api/reporting/v1/generate/${isImmediate ? 'immediate/' : ''}csv/saved-object/${id}`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({ timerange, state });
|
||||
},
|
||||
};
|
||||
|
||||
describe('Generation from Saved Search ID', () => {
|
||||
after(async () => {
|
||||
await reportingAPI.deleteAllReports();
|
||||
});
|
||||
|
||||
describe('Saved Search Features', () => {
|
||||
it('With filters and timebased data, explicit UTC format', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/logs');
|
||||
await esArchiver.load('logstash_functional');
|
||||
|
||||
const res = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7',
|
||||
{
|
||||
timerange: {
|
||||
timezone: 'UTC',
|
||||
min: '2015-09-19T10:00:00.000Z',
|
||||
max: '2015-09-21T10:00:00.000Z',
|
||||
},
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_UTC);
|
||||
|
||||
await esArchiver.unload('reporting/logs');
|
||||
await esArchiver.unload('logstash_functional');
|
||||
});
|
||||
|
||||
it('With filters and timebased data, default to UTC', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/logs');
|
||||
await esArchiver.load('logstash_functional');
|
||||
|
||||
const res = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7',
|
||||
{
|
||||
// @ts-expect-error: timerange.timezone is missing from post params
|
||||
timerange: {
|
||||
min: '2015-09-19T10:00:00.000Z',
|
||||
max: '2015-09-21T10:00:00.000Z',
|
||||
},
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_UTC);
|
||||
|
||||
await esArchiver.unload('reporting/logs');
|
||||
await esArchiver.unload('logstash_functional');
|
||||
});
|
||||
|
||||
it('With filters and timebased data, custom timezone', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/logs');
|
||||
await esArchiver.load('logstash_functional');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7',
|
||||
{
|
||||
timerange: {
|
||||
timezone: 'America/Phoenix',
|
||||
min: '2015-09-19T10:00:00.000Z',
|
||||
max: '2015-09-21T10:00:00.000Z',
|
||||
},
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_TIMEBASED_CUSTOM);
|
||||
|
||||
await esArchiver.unload('reporting/logs');
|
||||
await esArchiver.unload('logstash_functional');
|
||||
});
|
||||
|
||||
it('With filters and non-timebased data', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/sales');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:71e3ee20-3f99-11e9-b8ee-6b9604f2f877',
|
||||
{
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_TIMELESS);
|
||||
|
||||
await esArchiver.unload('reporting/sales');
|
||||
});
|
||||
|
||||
it('With scripted fields and field formatters', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/scripted_small2');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:a6d51430-ace2-11ea-815f-39e12f89a8c2',
|
||||
{
|
||||
timerange: {
|
||||
timezone: 'UTC',
|
||||
min: '1979-01-01T10:00:00Z',
|
||||
max: '1981-01-01T10:00:00Z',
|
||||
},
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED);
|
||||
|
||||
await esArchiver.unload('reporting/scripted_small2');
|
||||
});
|
||||
|
||||
it('Formatted date_nanos data, UTC timezone', async () => {
|
||||
await esArchiver.load('reporting/nanos');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:e4035040-a295-11e9-a900-ef10e0ac769e',
|
||||
{
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_NANOS);
|
||||
|
||||
await esArchiver.unload('reporting/nanos');
|
||||
});
|
||||
|
||||
it('Formatted date_nanos data, custom time zone', async () => {
|
||||
await esArchiver.load('reporting/nanos');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:e4035040-a295-11e9-a900-ef10e0ac769e',
|
||||
{
|
||||
state: {},
|
||||
timerange: { timezone: 'America/New_York' },
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_NANOS_CUSTOM);
|
||||
|
||||
await esArchiver.unload('reporting/nanos');
|
||||
});
|
||||
});
|
||||
|
||||
describe('API Features', () => {
|
||||
it('Return a 404', async () => {
|
||||
const { body } = (await generateAPI.getCsvFromSavedSearch('search:gobbledygook', {
|
||||
timerange: { timezone: 'UTC', min: 63097200000, max: 126255599999 },
|
||||
state: {},
|
||||
})) as supertest.Response;
|
||||
const expectedBody = {
|
||||
error: 'Not Found',
|
||||
message: 'Saved object [search/gobbledygook] not found',
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(body).to.eql(expectedBody);
|
||||
});
|
||||
|
||||
it('Return 400 if time range param is needed but missing', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/logs');
|
||||
await esArchiver.load('logstash_functional');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:d7a79750-3edd-11e9-99cc-4d80163ee9e7',
|
||||
{ state: {} }
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(400);
|
||||
expect(resType).to.eql('application/json');
|
||||
const { message: errorMessage } = JSON.parse(resText);
|
||||
expect(errorMessage).to.eql(
|
||||
'Time range params are required for index pattern [logstash-*], using time field [@timestamp]'
|
||||
);
|
||||
|
||||
await esArchiver.unload('reporting/logs');
|
||||
await esArchiver.unload('logstash_functional');
|
||||
});
|
||||
|
||||
it('Stops at Max Size Reached', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/hugedata');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:f34bf440-5014-11e9-bce7-4dabcb8bef24',
|
||||
{
|
||||
timerange: {
|
||||
timezone: 'UTC',
|
||||
min: '1960-01-01T10:00:00Z',
|
||||
max: '1999-01-01T10:00:00Z',
|
||||
},
|
||||
state: {},
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_HUGE);
|
||||
|
||||
await esArchiver.unload('reporting/hugedata');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Merge user state into the query', () => {
|
||||
it('for query', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/scripted_small2');
|
||||
|
||||
const params = {
|
||||
searchId: 'search:a6d51430-ace2-11ea-815f-39e12f89a8c2',
|
||||
postPayload: {
|
||||
timerange: { timezone: 'UTC', min: '1979-01-01T10:00:00Z', max: '1981-01-01T10:00:00Z' }, // prettier-ignore
|
||||
state: { query: { bool: { filter: [ { bool: { filter: [ { bool: { minimum_should_match: 1, should: [{ query_string: { fields: ['name'], query: 'Fel*' } }] } } ] } } ] } } }, // prettier-ignore
|
||||
},
|
||||
isImmediate: true,
|
||||
};
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
params.searchId,
|
||||
params.postPayload,
|
||||
params.isImmediate
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED_REQUERY);
|
||||
|
||||
await esArchiver.unload('reporting/scripted_small2');
|
||||
});
|
||||
|
||||
it('for sort', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/hugedata');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
'search:f34bf440-5014-11e9-bce7-4dabcb8bef24',
|
||||
{
|
||||
timerange: {
|
||||
timezone: 'UTC',
|
||||
min: '1979-01-01T10:00:00Z',
|
||||
max: '1981-01-01T10:00:00Z',
|
||||
},
|
||||
state: { sort: [{ name: { order: 'asc', unmapped_type: 'boolean' } }] },
|
||||
}
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_SCRIPTED_RESORTED);
|
||||
|
||||
await esArchiver.unload('reporting/hugedata');
|
||||
});
|
||||
|
||||
it('for docvalue_fields', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
||||
const params = {
|
||||
searchId: 'search:6091ead0-1c6d-11ea-a100-8589bb9d7c6b',
|
||||
postPayload: {
|
||||
timerange: {
|
||||
min: '2019-05-28T00:00:00Z',
|
||||
max: '2019-06-26T00:00:00Z',
|
||||
timezone: 'UTC',
|
||||
},
|
||||
state: {
|
||||
sort: [
|
||||
{ order_date: { order: 'desc', unmapped_type: 'boolean' } },
|
||||
{ order_id: { order: 'asc', unmapped_type: 'boolean' } },
|
||||
],
|
||||
docvalue_fields: [
|
||||
{ field: 'customer_birth_date', format: 'date_time' },
|
||||
{ field: 'order_date', format: 'date_time' },
|
||||
{ field: 'products.created_on', format: 'date_time' },
|
||||
],
|
||||
query: {
|
||||
bool: {
|
||||
must: [],
|
||||
filter: [
|
||||
{ match_all: {} },
|
||||
{ match_all: {} },
|
||||
{
|
||||
range: {
|
||||
order_date: {
|
||||
gte: '2019-05-28T00:00:00.000Z',
|
||||
lte: '2019-06-26T00:00:00.000Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
should: [],
|
||||
must_not: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isImmediate: true,
|
||||
};
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCsvFromSavedSearch(
|
||||
params.searchId,
|
||||
params.postPayload,
|
||||
params.isImmediate
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expect(resText).to.eql(fixtures.CSV_RESULT_DOCVALUE);
|
||||
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import supertest from 'supertest';
|
||||
import { JobParamsDownloadCSV } from '../../../plugins/reporting/server/export_types/csv_searchsource_immediate/types';
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
const getMockJobParams = (obj: Partial<JobParamsDownloadCSV>): JobParamsDownloadCSV => ({
|
||||
title: `Mock CSV Title`,
|
||||
...(obj as any),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const supertestSvc = getService('supertest');
|
||||
const reportingAPI = getService('reportingAPI');
|
||||
|
||||
const generateAPI = {
|
||||
getCSVFromSearchSource: async (job: JobParamsDownloadCSV) => {
|
||||
return await supertestSvc
|
||||
.post(`/api/reporting/v1/generate/immediate/csv_searchsource`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(job);
|
||||
},
|
||||
};
|
||||
|
||||
describe('CSV Generation from SearchSource', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
'csv:quoteValues': false,
|
||||
'dateFormat:tz': 'UTC',
|
||||
defaultIndex: 'logstash-*',
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await reportingAPI.deleteAllReports();
|
||||
});
|
||||
|
||||
it('Exports CSV with almost all fields when using fieldsFromSource', async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
index: '5193f870-d861-11e9-a311-0fa548c5f953',
|
||||
sort: [{ order_date: 'desc' }],
|
||||
fieldsFromSource: [
|
||||
'_id',
|
||||
'_index',
|
||||
'_score',
|
||||
'_source',
|
||||
'_type',
|
||||
'category',
|
||||
'category.keyword',
|
||||
'currency',
|
||||
'customer_birth_date',
|
||||
'customer_first_name',
|
||||
'customer_first_name.keyword',
|
||||
'customer_full_name',
|
||||
'customer_full_name.keyword',
|
||||
'customer_gender',
|
||||
'customer_id',
|
||||
'customer_last_name',
|
||||
'customer_last_name.keyword',
|
||||
'customer_phone',
|
||||
'day_of_week',
|
||||
'day_of_week_i',
|
||||
'email',
|
||||
'geoip.city_name',
|
||||
'geoip.continent_name',
|
||||
'geoip.country_iso_code',
|
||||
'geoip.location',
|
||||
'geoip.region_name',
|
||||
'manufacturer',
|
||||
'manufacturer.keyword',
|
||||
'order_date',
|
||||
'order_id',
|
||||
'products._id',
|
||||
'products._id.keyword',
|
||||
'products.base_price',
|
||||
'products.base_unit_price',
|
||||
'products.category',
|
||||
'products.category.keyword',
|
||||
'products.created_on',
|
||||
'products.discount_amount',
|
||||
'products.discount_percentage',
|
||||
'products.manufacturer',
|
||||
'products.manufacturer.keyword',
|
||||
'products.min_price',
|
||||
'products.price',
|
||||
'products.product_id',
|
||||
'products.product_name',
|
||||
'products.product_name.keyword',
|
||||
'products.quantity',
|
||||
'products.sku',
|
||||
'products.tax_amount',
|
||||
'products.taxful_price',
|
||||
'products.taxless_price',
|
||||
'products.unit_discount_amount',
|
||||
'sku',
|
||||
'taxful_total_price',
|
||||
'taxless_total_price',
|
||||
'total_quantity',
|
||||
'total_unique_products',
|
||||
'type',
|
||||
'user',
|
||||
],
|
||||
filter: [],
|
||||
parent: {
|
||||
query: { language: 'kuery', query: '' },
|
||||
filter: [],
|
||||
parent: {
|
||||
filter: [
|
||||
{
|
||||
meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} },
|
||||
range: {
|
||||
order_date: {
|
||||
gte: '2019-03-23T03:06:17.785Z',
|
||||
lte: '2019-10-04T02:33:16.708Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
browserTimezone: 'UTC',
|
||||
title: 'testfooyu78yt90-',
|
||||
})
|
||||
)) as supertest.Response;
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
|
||||
it('Exports CSV with all fields when using defaults', async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
index: '5193f870-d861-11e9-a311-0fa548c5f953',
|
||||
sort: [{ order_date: 'desc' }],
|
||||
fields: ['*'],
|
||||
filter: [],
|
||||
parent: {
|
||||
query: { language: 'kuery', query: '' },
|
||||
filter: [],
|
||||
parent: {
|
||||
filter: [
|
||||
{
|
||||
meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} },
|
||||
range: {
|
||||
order_date: {
|
||||
gte: '2019-03-23T03:06:17.785Z',
|
||||
lte: '2019-10-04T02:33:16.708Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
browserTimezone: 'UTC',
|
||||
title: 'testfooyu78yt90-',
|
||||
})
|
||||
)) as supertest.Response;
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
|
||||
it('Logs the error explanation if the search query returns an error', async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
||||
const { status: resStatus, text: resText } = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
index: '5193f870-d861-11e9-a311-0fa548c5f953',
|
||||
sort: [{ order_date: 'desc' }],
|
||||
fields: ['order_date', 'products'], // products is a non-leaf field
|
||||
filter: [],
|
||||
parent: {
|
||||
query: { language: 'kuery', query: '' },
|
||||
filter: [],
|
||||
parent: {
|
||||
filter: [
|
||||
{
|
||||
meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} },
|
||||
range: {
|
||||
order_date: {
|
||||
gte: '2019-03-23T03:06:17.785Z',
|
||||
lte: '2019-10-04T02:33:16.708Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
browserTimezone: 'UTC',
|
||||
title: 'testfooyu78yt90-',
|
||||
})
|
||||
)) as supertest.Response;
|
||||
expect(resStatus).to.eql(500);
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
|
||||
describe('date formatting', () => {
|
||||
before(async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/logs');
|
||||
await esArchiver.load('logstash_functional');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('reporting/logs');
|
||||
await esArchiver.unload('logstash_functional');
|
||||
});
|
||||
|
||||
it('With filters and timebased data, default to UTC', async () => {
|
||||
const res = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
fields: ['@timestamp', 'clientip', 'extension'],
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2015-09-20T10:19:40.307Z',
|
||||
lt: '2015-09-20T10:26:56.221Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
format: 'strict_date_optional_time',
|
||||
gte: '2015-01-12T07:00:55.654Z',
|
||||
lte: '2016-01-29T21:08:10.881Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
index: 'logstash-*',
|
||||
query: { language: 'kuery', query: '' },
|
||||
sort: [{ '@timestamp': 'desc' }],
|
||||
},
|
||||
})
|
||||
)) as supertest.Response;
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
});
|
||||
|
||||
it('With filters and timebased data, non-default timezone', async () => {
|
||||
const res = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
browserTimezone: 'America/Phoenix',
|
||||
searchSource: {
|
||||
fields: ['@timestamp', 'clientip', 'extension'],
|
||||
filter: [
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: '2015-09-20T10:19:40.307Z',
|
||||
lt: '2015-09-20T10:26:56.221Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
'@timestamp': {
|
||||
format: 'strict_date_optional_time',
|
||||
gte: '2015-01-12T07:00:55.654Z',
|
||||
lte: '2016-01-29T21:08:10.881Z',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
index: 'logstash-*',
|
||||
query: { language: 'kuery', query: '' },
|
||||
sort: [{ '@timestamp': 'desc' }],
|
||||
},
|
||||
})
|
||||
)) as supertest.Response;
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
});
|
||||
|
||||
it('Formatted date_nanos data, UTC timezone', async () => {
|
||||
await esArchiver.load('reporting/nanos');
|
||||
|
||||
const res = await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
version: true,
|
||||
index: '907bc200-a294-11e9-a900-ef10e0ac769e',
|
||||
sort: [{ date: 'desc' }],
|
||||
fields: ['date', 'message'],
|
||||
filter: [],
|
||||
},
|
||||
})
|
||||
);
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/nanos');
|
||||
});
|
||||
|
||||
it('Formatted date_nanos data, custom timezone (New York)', async () => {
|
||||
await esArchiver.load('reporting/nanos');
|
||||
|
||||
const res = await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
browserTimezone: 'America/New_York',
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
version: true,
|
||||
index: '907bc200-a294-11e9-a900-ef10e0ac769e',
|
||||
sort: [{ date: 'desc' }],
|
||||
fields: ['date', 'message'],
|
||||
filter: [],
|
||||
},
|
||||
})
|
||||
);
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/nanos');
|
||||
});
|
||||
});
|
||||
|
||||
describe('non-timebased', () => {
|
||||
it('Handle _id and _index columns', async () => {
|
||||
await esArchiver.load('reporting/nanos');
|
||||
|
||||
const res = await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
version: true,
|
||||
index: '907bc200-a294-11e9-a900-ef10e0ac769e',
|
||||
sort: [{ date: 'desc' }],
|
||||
fields: ['date', 'message', '_id', '_index'],
|
||||
filter: [],
|
||||
},
|
||||
})
|
||||
);
|
||||
const { status: resStatus, text: resText, type: resType } = res;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/nanos');
|
||||
});
|
||||
|
||||
it('With filters and non-timebased data', async () => {
|
||||
// load test data that contains a saved search and documents
|
||||
await esArchiver.load('reporting/sales');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
query: { query: '', language: 'kuery' },
|
||||
version: true,
|
||||
index: 'timeless-sales',
|
||||
sort: [{ power: 'asc' }],
|
||||
fields: ['name', 'power'],
|
||||
filter: [
|
||||
{
|
||||
range: { power: { gte: 1, lt: null } },
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/sales');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
it('Return a 404', async () => {
|
||||
const { body } = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
index: 'gobbledygook',
|
||||
},
|
||||
})
|
||||
)) as supertest.Response;
|
||||
const expectedBody = {
|
||||
error: 'Not Found',
|
||||
message: 'Saved object [index-pattern/gobbledygook] not found',
|
||||
statusCode: 404,
|
||||
};
|
||||
expect(body).to.eql(expectedBody);
|
||||
});
|
||||
|
||||
it(`Searches large amount of data, stops at Max Size Reached`, async () => {
|
||||
await esArchiver.load('reporting/ecommerce');
|
||||
await esArchiver.load('reporting/ecommerce_kibana');
|
||||
|
||||
const {
|
||||
status: resStatus,
|
||||
text: resText,
|
||||
type: resType,
|
||||
} = (await generateAPI.getCSVFromSearchSource(
|
||||
getMockJobParams({
|
||||
searchSource: {
|
||||
version: true,
|
||||
query: { query: '', language: 'kuery' },
|
||||
index: '5193f870-d861-11e9-a311-0fa548c5f953',
|
||||
sort: [{ order_date: 'desc' }],
|
||||
fields: [
|
||||
'order_date',
|
||||
'category',
|
||||
'currency',
|
||||
'customer_id',
|
||||
'order_id',
|
||||
'day_of_week_i',
|
||||
'products.created_on',
|
||||
'sku',
|
||||
],
|
||||
filter: [],
|
||||
parent: {
|
||||
query: { language: 'kuery', query: '' },
|
||||
filter: [],
|
||||
parent: {
|
||||
filter: [
|
||||
{
|
||||
meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} },
|
||||
range: {
|
||||
order_date: {
|
||||
gte: '2019-03-23T03:06:17.785Z',
|
||||
lte: '2019-10-04T02:33:16.708Z',
|
||||
format: 'strict_date_optional_time',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
browserTimezone: 'UTC',
|
||||
title: 'Ecommerce Data',
|
||||
})
|
||||
)) as supertest.Response;
|
||||
|
||||
expect(resStatus).to.eql(200);
|
||||
expect(resType).to.eql('text/csv');
|
||||
expectSnapshot(resText).toMatch();
|
||||
|
||||
await esArchiver.unload('reporting/ecommerce');
|
||||
await esArchiver.unload('reporting/ecommerce_kibana');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -12,7 +12,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('Reporting APIs', function () {
|
||||
this.tags('ciGroup2');
|
||||
loadTestFile(require.resolve('./csv_job_params'));
|
||||
loadTestFile(require.resolve('./csv_saved_search'));
|
||||
loadTestFile(require.resolve('./csv_searchsource_immediate'));
|
||||
loadTestFile(require.resolve('./network_policy'));
|
||||
loadTestFile(require.resolve('./spaces'));
|
||||
loadTestFile(require.resolve('./usage'));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue