{"version":3,"file":"queryResponse.mjs","sources":["../../../src/utils/queryResponse.ts"],"sourcesContent":["import {\n  DataQueryResponse,\n  KeyValue,\n  LoadingState,\n  DataQueryError,\n  TimeSeries,\n  TableData,\n  toDataFrame,\n  DataFrame,\n  MetricFindValue,\n  FieldType,\n  DataQuery,\n  DataFrameJSON,\n  dataFrameFromJSON,\n  QueryResultMetaNotice,\n} from '@grafana/data';\n\nimport { FetchError, FetchResponse } from '../services';\n\nimport { HealthCheckResultDetails } from './DataSourceWithBackend';\nimport { toDataQueryError } from './toDataQueryError';\n\nexport const cachedResponseNotice: QueryResultMetaNotice = { severity: 'info', text: 'Cached response' };\n\n/**\n * Single response object from a backend data source. Properties are optional but response should contain at least\n * an error or a some data (but can contain both). Main way to send data is with dataframes attribute as series and\n * tables data attributes are legacy formats.\n *\n * @internal\n */\nexport interface DataResponse {\n  error?: string;\n  refId?: string;\n  frames?: DataFrameJSON[];\n  status?: number;\n\n  // Legacy TSDB format...\n  series?: TimeSeries[];\n  tables?: TableData[];\n}\n\n/**\n * This is the type of response expected form backend datasource.\n *\n * @internal\n */\nexport interface BackendDataSourceResponse {\n  results: KeyValue<DataResponse>;\n}\n\n/**\n * Parse the results from /api/ds/query into a DataQueryResponse\n *\n * @param res - the HTTP response data.\n * @param queries - optional DataQuery array that will order the response based on the order of query refId's.\n *\n * @public\n */\nexport function toDataQueryResponse(\n  res:\n    | { data: BackendDataSourceResponse | undefined }\n    | FetchResponse<BackendDataSourceResponse | undefined>\n    | DataQueryError,\n  queries?: DataQuery[]\n): DataQueryResponse {\n  const rsp: DataQueryResponse = { data: [], state: LoadingState.Done };\n\n  const traceId = 'traceId' in res ? res.traceId : undefined;\n\n  if (traceId != null) {\n    rsp.traceIds = [traceId];\n  }\n\n  // If the response isn't in a correct shape we just ignore the data and pass empty DataQueryResponse.\n  const fetchResponse = res as FetchResponse;\n  if (fetchResponse.data?.results) {\n    const results = fetchResponse.data.results;\n    const refIDs = queries?.length ? queries.map((q) => q.refId) : Object.keys(results);\n    const cachedResponse = isCachedResponse(fetchResponse);\n    const data: DataResponse[] = [];\n\n    for (const refId of refIDs) {\n      const dr = results[refId];\n      if (!dr) {\n        continue;\n      }\n      dr.refId = refId;\n      data.push(dr);\n    }\n\n    for (const dr of data) {\n      if (dr.error) {\n        const errorObj: DataQueryError = {\n          refId: dr.refId,\n          message: dr.error,\n          status: dr.status,\n        };\n        if (traceId != null) {\n          errorObj.traceId = traceId;\n        }\n        if (!rsp.error) {\n          rsp.error = { ...errorObj };\n        }\n        if (rsp.errors) {\n          rsp.errors.push({ ...errorObj });\n        } else {\n          rsp.errors = [{ ...errorObj }];\n        }\n        rsp.state = LoadingState.Error;\n      }\n\n      if (dr.frames?.length) {\n        for (let js of dr.frames) {\n          if (cachedResponse) {\n            js = addCacheNotice(js);\n          }\n          const df = dataFrameFromJSON(js);\n          if (!df.refId) {\n            df.refId = dr.refId;\n          }\n          rsp.data.push(df);\n        }\n        continue; // the other tests are legacy\n      }\n\n      if (dr.series?.length) {\n        for (const s of dr.series) {\n          if (!s.refId) {\n            s.refId = dr.refId;\n          }\n          rsp.data.push(toDataFrame(s));\n        }\n      }\n\n      if (dr.tables?.length) {\n        for (const s of dr.tables) {\n          if (!s.refId) {\n            s.refId = dr.refId;\n          }\n          rsp.data.push(toDataFrame(s));\n        }\n      }\n    }\n  }\n\n  // When it is not an OK response, make sure the error gets added\n  if (fetchResponse.status && fetchResponse.status !== 200) {\n    if (rsp.state !== LoadingState.Error) {\n      rsp.state = LoadingState.Error;\n    }\n    if (!rsp.error) {\n      rsp.error = toDataQueryError(res);\n    }\n  }\n\n  return rsp;\n}\n\nfunction isCachedResponse(res: FetchResponse<BackendDataSourceResponse | undefined>): boolean {\n  const headers = res?.headers;\n  if (!headers || !headers.get) {\n    return false;\n  }\n  return headers.get('X-Cache') === 'HIT';\n}\n\nfunction addCacheNotice(frame: DataFrameJSON): DataFrameJSON {\n  return {\n    ...frame,\n    schema: {\n      ...frame.schema,\n      fields: [...(frame.schema?.fields ?? [])],\n      meta: {\n        ...frame.schema?.meta,\n        notices: [...(frame.schema?.meta?.notices ?? []), cachedResponseNotice],\n        isCachedResponse: true,\n      },\n    },\n  };\n}\n\nexport interface TestingStatus {\n  message?: string | null;\n  status?: string | null;\n  details?: HealthCheckResultDetails;\n}\n\n/**\n * Data sources using api/ds/query to test data sources can use this function to\n * handle errors and convert them to TestingStatus object.\n *\n * If possible, this should be avoided in favor of implementing /health endpoint\n * and testing data source with DataSourceWithBackend.testDataSource()\n *\n * Re-thrown errors are handled by testDataSource() in public/app/features/datasources/state/actions.ts\n *\n * @returns {TestingStatus}\n */\nexport function toTestingStatus(err: FetchError): TestingStatus {\n  const queryResponse = toDataQueryResponse(err);\n  // POST api/ds/query errors returned as { message: string, error: string } objects\n  if (queryResponse.error?.data?.message) {\n    return {\n      status: 'error',\n      message: queryResponse.error.data.message,\n      details: queryResponse.error?.data?.error ? { message: queryResponse.error.data.error } : undefined,\n    };\n  }\n  // POST api/ds/query errors returned in results object\n  else if (queryResponse.error?.refId && queryResponse.error?.message) {\n    return {\n      status: 'error',\n      message: queryResponse.error.message,\n    };\n  }\n\n  throw err;\n}\n\n/**\n * Return the first string or non-time field as the value\n *\n * @beta\n */\nexport function frameToMetricFindValue(frame: DataFrame): MetricFindValue[] {\n  if (!frame || !frame.length) {\n    return [];\n  }\n\n  const values: MetricFindValue[] = [];\n  let field = frame.fields.find((f) => f.type === FieldType.string);\n  if (!field) {\n    field = frame.fields.find((f) => f.type !== FieldType.time);\n  }\n  if (field) {\n    for (let i = 0; i < field.values.length; i++) {\n      values.push({ text: '' + field.values[i] });\n    }\n  }\n  return values;\n}\n"],"names":[],"mappings":";;;AAsBO,MAAM,oBAA8C,GAAA,EAAE,QAAU,EAAA,MAAA,EAAQ,MAAM,iBAAkB;AAqCvF,SAAA,mBAAA,CACd,KAIA,OACmB,EAAA;AAjErB,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAkEE,EAAA,MAAM,MAAyB,EAAE,IAAA,EAAM,EAAI,EAAA,KAAA,EAAO,aAAa,IAAK,EAAA;AAEpE,EAAA,MAAM,OAAU,GAAA,SAAA,IAAa,GAAM,GAAA,GAAA,CAAI,OAAU,GAAA,SAAA;AAEjD,EAAA,IAAI,WAAW,IAAM,EAAA;AACnB,IAAI,GAAA,CAAA,QAAA,GAAW,CAAC,OAAO,CAAA;AAAA;AAIzB,EAAA,MAAM,aAAgB,GAAA,GAAA;AACtB,EAAI,IAAA,CAAA,EAAA,GAAA,aAAA,CAAc,IAAd,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAoB,OAAS,EAAA;AAC/B,IAAM,MAAA,OAAA,GAAU,cAAc,IAAK,CAAA,OAAA;AACnC,IAAA,MAAM,MAAS,GAAA,CAAA,OAAA,IAAA,IAAA,GAAA,SAAA,GAAA,OAAA,CAAS,MAAS,IAAA,OAAA,CAAQ,GAAI,CAAA,CAAC,CAAM,KAAA,CAAA,CAAE,KAAK,CAAA,GAAI,MAAO,CAAA,IAAA,CAAK,OAAO,CAAA;AAClF,IAAM,MAAA,cAAA,GAAiB,iBAAiB,aAAa,CAAA;AACrD,IAAA,MAAM,OAAuB,EAAC;AAE9B,IAAA,KAAA,MAAW,SAAS,MAAQ,EAAA;AAC1B,MAAM,MAAA,EAAA,GAAK,QAAQ,KAAK,CAAA;AACxB,MAAA,IAAI,CAAC,EAAI,EAAA;AACP,QAAA;AAAA;AAEF,MAAA,EAAA,CAAG,KAAQ,GAAA,KAAA;AACX,MAAA,IAAA,CAAK,KAAK,EAAE,CAAA;AAAA;AAGd,IAAA,KAAA,MAAW,MAAM,IAAM,EAAA;AACrB,MAAA,IAAI,GAAG,KAAO,EAAA;AACZ,QAAA,MAAM,QAA2B,GAAA;AAAA,UAC/B,OAAO,EAAG,CAAA,KAAA;AAAA,UACV,SAAS,EAAG,CAAA,KAAA;AAAA,UACZ,QAAQ,EAAG,CAAA;AAAA,SACb;AACA,QAAA,IAAI,WAAW,IAAM,EAAA;AACnB,UAAA,QAAA,CAAS,OAAU,GAAA,OAAA;AAAA;AAErB,QAAI,IAAA,CAAC,IAAI,KAAO,EAAA;AACd,UAAI,GAAA,CAAA,KAAA,GAAQ,EAAE,GAAG,QAAS,EAAA;AAAA;AAE5B,QAAA,IAAI,IAAI,MAAQ,EAAA;AACd,UAAA,GAAA,CAAI,MAAO,CAAA,IAAA,CAAK,EAAE,GAAG,UAAU,CAAA;AAAA,SAC1B,MAAA;AACL,UAAA,GAAA,CAAI,MAAS,GAAA,CAAC,EAAE,GAAG,UAAU,CAAA;AAAA;AAE/B,QAAA,GAAA,CAAI,QAAQ,YAAa,CAAA,KAAA;AAAA;AAG3B,MAAI,IAAA,CAAA,EAAA,GAAA,EAAA,CAAG,MAAH,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAW,MAAQ,EAAA;AACrB,QAAS,KAAA,IAAA,EAAA,IAAM,GAAG,MAAQ,EAAA;AACxB,UAAA,IAAI,cAAgB,EAAA;AAClB,YAAA,EAAA,GAAK,eAAe,EAAE,CAAA;AAAA;AAExB,UAAM,MAAA,EAAA,GAAK,kBAAkB,EAAE,CAAA;AAC/B,UAAI,IAAA,CAAC,GAAG,KAAO,EAAA;AACb,YAAA,EAAA,CAAG,QAAQ,EAAG,CAAA,KAAA;AAAA;AAEhB,UAAI,GAAA,CAAA,IAAA,CAAK,KAAK,EAAE,CAAA;AAAA;AAElB,QAAA;AAAA;AAGF,MAAI,IAAA,CAAA,EAAA,GAAA,EAAA,CAAG,MAAH,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAW,MAAQ,EAAA;AACrB,QAAW,KAAA,MAAA,CAAA,IAAK,GAAG,MAAQ,EAAA;AACzB,UAAI,IAAA,CAAC,EAAE,KAAO,EAAA;AACZ,YAAA,CAAA,CAAE,QAAQ,EAAG,CAAA,KAAA;AAAA;AAEf,UAAA,GAAA,CAAI,IAAK,CAAA,IAAA,CAAK,WAAY,CAAA,CAAC,CAAC,CAAA;AAAA;AAC9B;AAGF,MAAI,IAAA,CAAA,EAAA,GAAA,EAAA,CAAG,MAAH,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAW,MAAQ,EAAA;AACrB,QAAW,KAAA,MAAA,CAAA,IAAK,GAAG,MAAQ,EAAA;AACzB,UAAI,IAAA,CAAC,EAAE,KAAO,EAAA;AACZ,YAAA,CAAA,CAAE,QAAQ,EAAG,CAAA,KAAA;AAAA;AAEf,UAAA,GAAA,CAAI,IAAK,CAAA,IAAA,CAAK,WAAY,CAAA,CAAC,CAAC,CAAA;AAAA;AAC9B;AACF;AACF;AAIF,EAAA,IAAI,aAAc,CAAA,MAAA,IAAU,aAAc,CAAA,MAAA,KAAW,GAAK,EAAA;AACxD,IAAI,IAAA,GAAA,CAAI,KAAU,KAAA,YAAA,CAAa,KAAO,EAAA;AACpC,MAAA,GAAA,CAAI,QAAQ,YAAa,CAAA,KAAA;AAAA;AAE3B,IAAI,IAAA,CAAC,IAAI,KAAO,EAAA;AACd,MAAI,GAAA,CAAA,KAAA,GAAQ,iBAAiB,GAAG,CAAA;AAAA;AAClC;AAGF,EAAO,OAAA,GAAA;AACT;AAEA,SAAS,iBAAiB,GAAoE,EAAA;AAC5F,EAAA,MAAM,UAAU,GAAK,IAAA,IAAA,GAAA,SAAA,GAAA,GAAA,CAAA,OAAA;AACrB,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,CAAQ,GAAK,EAAA;AAC5B,IAAO,OAAA,KAAA;AAAA;AAET,EAAO,OAAA,OAAA,CAAQ,GAAI,CAAA,SAAS,CAAM,KAAA,KAAA;AACpC;AAEA,SAAS,eAAe,KAAqC,EAAA;AAvK7D,EAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AAwKE,EAAO,OAAA;AAAA,IACL,GAAG,KAAA;AAAA,IACH,MAAQ,EAAA;AAAA,MACN,GAAG,KAAM,CAAA,MAAA;AAAA,MACT,MAAA,EAAQ,CAAC,GAAI,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,KAAA,CAAM,WAAN,IAAc,GAAA,SAAA,GAAA,EAAA,CAAA,MAAA,KAAd,IAAwB,GAAA,EAAA,GAAA,EAAG,CAAA;AAAA,MACxC,IAAM,EAAA;AAAA,QACJ,GAAA,CAAG,EAAM,GAAA,KAAA,CAAA,MAAA,KAAN,IAAc,GAAA,SAAA,GAAA,EAAA,CAAA,IAAA;AAAA,QACjB,OAAS,EAAA,CAAC,GAAI,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,KAAA,CAAM,MAAN,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAc,IAAd,KAAA,IAAA,GAAA,SAAA,GAAA,EAAA,CAAoB,OAApB,KAAA,IAAA,GAAA,EAAA,GAA+B,EAAC,EAAI,oBAAoB,CAAA;AAAA,QACtE,gBAAkB,EAAA;AAAA;AACpB;AACF,GACF;AACF;AA6CO,SAAS,uBAAuB,KAAqC,EAAA;AAC1E,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,CAAM,MAAQ,EAAA;AAC3B,IAAA,OAAO,EAAC;AAAA;AAGV,EAAA,MAAM,SAA4B,EAAC;AACnC,EAAI,IAAA,KAAA,GAAQ,MAAM,MAAO,CAAA,IAAA,CAAK,CAAC,CAAM,KAAA,CAAA,CAAE,IAAS,KAAA,SAAA,CAAU,MAAM,CAAA;AAChE,EAAA,IAAI,CAAC,KAAO,EAAA;AACV,IAAQ,KAAA,GAAA,KAAA,CAAM,OAAO,IAAK,CAAA,CAAC,MAAM,CAAE,CAAA,IAAA,KAAS,UAAU,IAAI,CAAA;AAAA;AAE5D,EAAA,IAAI,KAAO,EAAA;AACT,IAAA,KAAA,IAAS,IAAI,CAAG,EAAA,CAAA,GAAI,KAAM,CAAA,MAAA,CAAO,QAAQ,CAAK,EAAA,EAAA;AAC5C,MAAO,MAAA,CAAA,IAAA,CAAK,EAAE,IAAM,EAAA,EAAA,GAAK,MAAM,MAAO,CAAA,CAAC,GAAG,CAAA;AAAA;AAC5C;AAEF,EAAO,OAAA,MAAA;AACT;;;;"}