# All files / utils fluxParsing.ts

95.45% Statements 63/66
87.5% Branches 7/8
95% Functions 19/20
94.64% Lines 53/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    1492x
    2x

    2x
    2x

    2x

    2x
    12x

    12x

    12x

    21x

    12x

    2x
    7x
    7x
    11x

    6x

    2x
    11x
    11x
    61x

    11x
    1x

    10x
    59x

    10x

    1x

    9x
    9x
    9x
    27x
    18x

    9x

    20x

    9x

    15x
    27x
    12x

    9x
    102x
    39x

    63x

    9x
    14x

    14x

    14x
    65x

    14x
    65x
    65x

    14x
    163x

    14x

    9x

     import Papa from 'papaparse'
    import uuid from 'uuid'

    import {FluxTable} from '../types'
    import {get} from './get'
    import {groupBy} from './groupBy'

    export const parseResponseError = (response: string): FluxTable[] => {
      const data = Papa.parse(response.trim()).data as string[][]

      return [
        {
          id: uuid.v4(),
          name: 'Error',
          result: '',
          groupKey: {},
          dataTypes: {},
          data,
        },
      ]
    }

    /*
      A Flux CSV response can contain multiple CSV files each joined by a newline.
      This function splits up a CSV response into these individual CSV files.

      See https://github.com/influxdata/flux/blob/master/docs/SPEC.md#multiple-tables.
    */
    export const parseChunks = (response: string): string[] => {
      const trimmedResponse = response.trim()

      Iif (trimmedResponse === '') {
        return []
      }

      // Split the response into separate chunks whenever we encounter:
      //
      // 1. A newline
      // 2. Followed by any amount of whitespace
      // 3. Followed by a newline
      // 4. Followed by a `#` character
      //
      // The last condition is [necessary][0] for handling CSV responses with
      // values containing newlines.
      //
      // [0]: https://github.com/influxdata/influxdb/issues/15017

      const chunks = trimmedResponse
        .split(/\n\s*\n#/)
        .map((s, i) => (i === 0 ? s : `#${s}`))

      return chunks
    }

    export const parseResponse = (response: string): FluxTable[] => {
      const chunks = parseChunks(response)
      const tables = chunks.reduce((acc, chunk) => {
        return [...acc, ...parseTables(chunk)]
      }, [])

      return tables
    }

    export const parseTables = (responseChunk: string): FluxTable[] => {
      const lines = responseChunk.split('\n')
      const annotationLines: string = lines
        .filter(line => line.startsWith('#'))
        .join('\n')
        .trim()

      if (!annotationLines) {
        throw new Error('Unable to extract annotation data')
      }

      const nonAnnotationLines: string = lines
        .filter(line => !line.startsWith('#'))
        .join('\n')
        .trim()

      if (!nonAnnotationLines) {
        // A response may be truncated on an arbitrary line. This guards against
        // the case where a response is truncated on annotation data
        return []
      }

      const nonAnnotationData = Papa.parse(nonAnnotationLines).data
      const annotationData = Papa.parse(annotationLines).data
      const headerRow = nonAnnotationData[0]
      const tableColIndex = headerRow.findIndex(h => h === 'table')
      const resultColIndex = headerRow.findIndex(h => h === 'result')

      interface TableGroup {
        [tableId: string]: string[]
      }

      const tableGroup: TableGroup = groupBy(
        nonAnnotationData.slice(1),
        row => row[tableColIndex]
      )
      // Group rows by their table id
      const tablesData = Object.values(tableGroup)

      const groupRow = annotationData.find(row => row[0] === '#group')
      const defaultsRow = annotationData.find(row => row[0] === '#default')
      const dataTypeRow = annotationData.find(row => row[0] === '#datatype')

      const groupKeyIndices = groupRow.reduce((acc, value, i) => {
        if (value === 'true') {
          return [...acc, i]
        }

        return acc
      }, [])

      const tables = tablesData.map(tableData => {
        const dataRow = get(tableData, '0', defaultsRow)

        const result: string =
          get(dataRow, resultColIndex, '') || get(defaultsRow, resultColIndex, '')

        const groupKey = groupKeyIndices.reduce((acc, i) => {
          return {...acc, [headerRow[i]]: get(dataRow, i, '')}
        }, {})

        const name = Object.entries(groupKey)
          .filter(([k]) => !['_start', '_stop'].includes(k))
          .map(([k, v]) => `${k}=${v}`)
          .join(' ')

        const dataTypes = dataTypeRow.reduce(
          (acc, dataType, i) => ({
            ...acc,
            [headerRow[i]]: dataType,
          }),
          {}
        )
        return {
          id: uuid.v4(),
          data: [[...headerRow], ...tableData],
          name,
          result,
          groupKey,
          dataTypes,
        } as FluxTable
      })

      return tables
    }