Add IFQL script response parser

Co-authored-by: Andrew Watkins <andrew.watkinz@gmail.com>
Co-authored-by: Chris Henn <chris.henn@influxdata.com>
pull/3519/head
Andrew Watkins 2018-05-23 11:30:52 -07:00
parent df94bf30d4
commit 2b37494dcb
5 changed files with 151 additions and 9 deletions

View File

@ -41,6 +41,7 @@
"@types/jest": "^22.1.4",
"@types/lodash": "^4.14.104",
"@types/node": "^9.4.6",
"@types/papaparse": "^4.1.34",
"@types/prop-types": "^15.5.2",
"@types/react": "^16.0.38",
"@types/react-dnd": "^2.0.36",
@ -133,6 +134,7 @@
"lodash": "^4.3.0",
"moment": "^2.13.0",
"nano-date": "^2.0.1",
"papaparse": "^4.4.0",
"prop-types": "^15.6.1",
"query-string": "^5.0.0",
"react": "^16.3.1",
@ -157,4 +159,4 @@
"rome": "^2.1.22",
"uuid": "^3.2.1"
}
}
}

View File

@ -1,3 +1,34 @@
export const parseTables = resp => {
return resp.split('\n\n')
import Papa from 'papaparse'
import _ from 'lodash'
interface ScriptResult {
name: string
data: string[][]
metadata: string[][]
}
export const parseResults = (resp: string): ScriptResult[] => {
return resp.split('\n\n').map(parseResult)
}
export const parseResult = (raw: string, index: number): ScriptResult => {
const lines = raw.split('\n')
const rawMetadata: string = lines
.filter(line => line.startsWith('#'))
.map(line => line.slice(1))
.join('\n')
const rawData: string = lines.filter(line => !line.startsWith('#')).join('\n')
const metadata = Papa.parse(rawMetadata).data
const data = Papa.parse(rawData).data
const headerRow = _.get(data, '0', [])
const measurementHeaderIndex = headerRow.findIndex(v => v === '_measurement')
const name = _.get(data, `1.${measurementHeaderIndex}`, `Result ${index}`)
return {
name,
data,
metadata,
}
}

View File

@ -1358,7 +1358,66 @@ export const rule = {
}
// prettier-ignore
export const FROM_LAST_RESPONSE = `#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
export const RESPONSE_METADATA = `#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
#partition,false,false,false,false,false,false,true,true,true,true
#default,_result,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,cpu,host
,,0,2018-05-23T17:42:29.536834648Z,2018-05-23T17:43:29.536834648Z,2018-05-23T17:42:29.654Z,0,usage_guest,cpu,cpu-total,WattsInfluxDB
`
export const RESPONSE_NO_METADATA = `,result,table,_start,_stop,_time,_value,_field,_measurement,cpu,host
,,0,2018-05-23T17:42:29.536834648Z,2018-05-23T17:43:29.536834648Z,2018-05-23T17:42:29.654Z,0,usage_guest,cpu,cpu-total,WattsInfluxDB
`
export const RESPONSE_NO_MEASUREMENT = `,result,table,_start,_stop,_time,_value,_field,cpu,host
,,0,2018-05-23T17:42:29.536834648Z,2018-05-23T17:43:29.536834648Z,2018-05-23T17:42:29.654Z,0,usage_guest,cpu-total,WattsInfluxDB`
export const EXPECTED_COLUMNS = [
'',
'result',
'table',
'_start',
'_stop',
'_time',
'_value',
'_field',
'_measurement',
'cpu',
'host',
]
export const EXPECTED_METADATA = [
[
'datatype',
'string',
'long',
'dateTime:RFC3339',
'dateTime:RFC3339',
'dateTime:RFC3339',
'double',
'string',
'string',
'string',
'string',
],
[
'partition',
'false',
'false',
'false',
'false',
'false',
'false',
'true',
'true',
'true',
'true',
],
['default', '_result', '', '', '', '', '', '', '', '', ''],
]
// prettier-ignore
export const LARGE_RESPONSE = `#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,double,string,string,string,string
#partition,false,false,false,false,false,false,true,true,true,true
#default,_result,,,,,,,,,
,result,table,_start,_stop,_time,_value,_field,_measurement,cpu,host

View File

@ -1,10 +1,52 @@
import {parseTables} from 'src/shared/parsing/ifql'
import {FROM_LAST_RESPONSE} from 'test/shared/parsing/constants'
import {parseResults} from 'src/shared/parsing/ifql'
import {
RESPONSE_NO_METADATA,
RESPONSE_METADATA,
RESPONSE_NO_MEASUREMENT,
LARGE_RESPONSE,
EXPECTED_METADATA,
EXPECTED_COLUMNS,
} from 'test/shared/parsing/constants'
describe('IFQL response parser', () => {
it('parseTables', () => {
const result = parseTables(FROM_LAST_RESPONSE)
it('parseResults into the right number of tables', () => {
const result = parseResults(LARGE_RESPONSE)
expect(result).toHaveLength(47)
})
describe('headers', () => {
it('can parse headers when no metadata is present', () => {
const actual = parseResults(RESPONSE_NO_METADATA)[0].data[0]
expect(actual).toEqual(EXPECTED_COLUMNS)
})
it('can parse headers when metadata is present', () => {
const actual = parseResults(RESPONSE_METADATA)[0].data[0]
expect(actual).toEqual(EXPECTED_COLUMNS)
})
it('returns the approriate metadata', () => {
const actual = parseResults(RESPONSE_METADATA)[0].metadata
expect(actual).toEqual(EXPECTED_METADATA)
})
})
describe('name', () => {
it('uses the measurement as a name when present', () => {
const actual = parseResults(RESPONSE_METADATA)[0].name
const expected = 'cpu'
expect(actual).toBe(expected)
})
it('uses the index as a name if a measurement column is not present', () => {
const actual = parseResults(RESPONSE_NO_MEASUREMENT)[0].name
const expected = 'Result 0'
expect(actual).toBe(expected)
})
})
})

View File

@ -65,6 +65,10 @@
version "9.6.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.6.tgz#439b91f9caf3983cad2eef1e11f6bedcbf9431d2"
"@types/papaparse@^4.1.34":
version "4.1.34"
resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-4.1.34.tgz#893628fbb70243313e46d1b962989c023184783b"
"@types/prop-types@*", "@types/prop-types@^15.5.2":
version "15.5.2"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.2.tgz#3c6b8dceb2906cc87fe4358e809f9d20c8d59be1"
@ -6297,6 +6301,10 @@ pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
papaparse@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-4.4.0.tgz#6bcdbda80873e00cfb0bdcd7a4571c72a9a40168"
parallel-transform@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"