diff --git a/packages/run-it/src/RunIt.tsx b/packages/run-it/src/RunIt.tsx index 93556c6ca..0125c82b2 100644 --- a/packages/run-it/src/RunIt.tsx +++ b/packages/run-it/src/RunIt.tsx @@ -168,8 +168,9 @@ export const RunIt: FC = ({ tabs.onSelectTab(1) if (sdk) { setLoading(true) - setResponseContent( - await runRequest( + let response: ResponseContent + try { + response = await runRequest( sdk, basePath, httpMethod, @@ -178,7 +179,18 @@ export const RunIt: FC = ({ queryParams, body ) - ) + } catch (err) { + // This should not happen but it could. runRequest uses + // sdk.ok to login once. sdk.ok throws an error so fake + // out the response so that something can be rendered. + response = { + ok: false, + statusMessage: err.message ? err.message : 'Unknown error!', + statusCode: -1, + body: JSON.stringify(err), + } as ResponseContent + } + setResponseContent(response) } } diff --git a/packages/run-it/src/components/DataGrid/gridUtils.spec.tsx b/packages/run-it/src/components/DataGrid/gridUtils.spec.tsx index a7d6ed89a..af1c92082 100644 --- a/packages/run-it/src/components/DataGrid/gridUtils.spec.tsx +++ b/packages/run-it/src/components/DataGrid/gridUtils.spec.tsx @@ -24,7 +24,7 @@ */ -import { gridHeaders, gridRows, parseCsv, parseJson } from './gridUtils' +import { gridHeaders, gridRows, parseCsv, json2Csv } from './gridUtils' const dataRowsLength = 2 const allRowsLength = dataRowsLength + 1 @@ -59,7 +59,7 @@ describe('gridUtils', () => { }) test('parses json data', () => { - const actual = parseJson(testJsonData) + const actual = json2Csv(testJsonData) expect(actual).toBeDefined() expect(actual.data).toBeDefined() expect(actual.data).toHaveLength(allRowsLength) @@ -80,7 +80,7 @@ describe('gridUtils', () => { }) test('creates grid rows from json', () => { - const data = parseJson(testJsonData) + const data = json2Csv(testJsonData) const actual = gridRows(data.data) expect(actual).toHaveLength(allRowsLength) }) diff --git a/packages/run-it/src/components/DataGrid/gridUtils.tsx b/packages/run-it/src/components/DataGrid/gridUtils.tsx index 44e8447cc..983c4a115 100644 --- a/packages/run-it/src/components/DataGrid/gridUtils.tsx +++ b/packages/run-it/src/components/DataGrid/gridUtils.tsx @@ -37,7 +37,7 @@ export const parseCsv = (content: string) => { return Papa.parse(content.trim()) } -export const parseJson = (content: any) => { +export const json2Csv = (content: any) => { const csv = Papa.unparse(content) return parseCsv(csv) } diff --git a/packages/run-it/src/components/DataGrid/index.ts b/packages/run-it/src/components/DataGrid/index.ts index 5016ce5c8..4aee5a1ca 100644 --- a/packages/run-it/src/components/DataGrid/index.ts +++ b/packages/run-it/src/components/DataGrid/index.ts @@ -24,4 +24,4 @@ */ export { DataGrid } from './DataGrid' -export { parseCsv, parseJson } from './gridUtils' +export { parseCsv, json2Csv } from './gridUtils' diff --git a/packages/run-it/src/components/ShowResponse/ShowResponse.spec.tsx b/packages/run-it/src/components/ShowResponse/ShowResponse.spec.tsx index 41db4a743..4293f4fc8 100644 --- a/packages/run-it/src/components/ShowResponse/ShowResponse.spec.tsx +++ b/packages/run-it/src/components/ShowResponse/ShowResponse.spec.tsx @@ -35,6 +35,7 @@ import { testJsonResponse, testTextResponse, testUnknownResponse, + testBogusJsonResponse, } from '../../test-data' import { ShowResponse } from './ShowResponse' @@ -97,4 +98,15 @@ describe('ShowResponse', () => { screen.getByText(testErrorResponse.body.toString(), { exact: false }) ).toBeInTheDocument() }) + + test('it renders bogus json responses', () => { + renderWithTheme() + expect(screen.getByText('200: application/json')).toBeInTheDocument() + expect( + screen.getByText( + 'The response body could not be parsed. Displaying raw data.' + ) + ).toBeInTheDocument() + expect(screen.getByText('I AM A LYING JSON RESPONSE')).toBeInTheDocument() + }) }) diff --git a/packages/run-it/src/components/ShowResponse/ShowResponse.tsx b/packages/run-it/src/components/ShowResponse/ShowResponse.tsx index 5b8e86706..8e694e7a8 100644 --- a/packages/run-it/src/components/ShowResponse/ShowResponse.tsx +++ b/packages/run-it/src/components/ShowResponse/ShowResponse.tsx @@ -28,7 +28,7 @@ import React, { FC } from 'react' import { Heading } from '@looker/components' import { IRawResponse } from '@looker/sdk-rtl' -import { pickResponseHandler } from './responseUtils' +import { pickResponseHandler, fallbackResponseHandler } from './responseUtils' interface ShowResponseProps { /** A basic HTTP response for "raw" HTTP requests */ @@ -47,7 +47,14 @@ export const ShowResponse: FC = ({ verb, path, }) => { - const pickedHandler = pickResponseHandler(response) + // Bullet proof the rendered response. If for some reason we get a bad response or bad data in the + // response, render something + let renderedResponse + try { + renderedResponse = pickResponseHandler(response).component(response) + } catch (err) { + renderedResponse = fallbackResponseHandler().component(response) + } // TODO make a badge for the verb. // Once we are satisfied with the badge in the api-explorer package it should be moved here @@ -56,7 +63,7 @@ export const ShowResponse: FC = ({ {`${verb || ''} ${path || ''} ${response.statusCode}: ${ response.contentType }`} - {pickedHandler && pickedHandler.component(response)} + {renderedResponse} ) } diff --git a/packages/run-it/src/components/ShowResponse/responseUtils.tsx b/packages/run-it/src/components/ShowResponse/responseUtils.tsx index 69a668652..40d3bf9fb 100644 --- a/packages/run-it/src/components/ShowResponse/responseUtils.tsx +++ b/packages/run-it/src/components/ShowResponse/responseUtils.tsx @@ -25,10 +25,10 @@ */ import React, { ReactElement } from 'react' import { IRawResponse, ResponseMode, responseMode } from '@looker/sdk-rtl' -import { Paragraph, CodeBlock } from '@looker/components' +import { Paragraph, CodeBlock, MessageBar } from '@looker/components' import { CodeDisplay } from '@looker/code-editor' -import { DataGrid, parseCsv, parseJson } from '../DataGrid' +import { DataGrid, parseCsv, json2Csv } from '../DataGrid' /** * Are all items this array "simple" @@ -63,21 +63,17 @@ export const isColumnar = (data: any[]) => { * * Shows the JSON in a syntax-highlighted fashion * If the JSON is parseable as 2D row/column data it will also be shown in grid + * If JSON cannot be parsed it will be show as is * @param response */ const ShowJSON = (response: IRawResponse) => { const content = response.body.toString() - const data = parseJson(content) + const data = json2Csv(content) const showGrid = isColumnar(data.data) - const raw = ( - - ) - if (!showGrid) return raw - return + const json = JSON.stringify(JSON.parse(response.body), null, 2) + const raw = + if (showGrid) return + return raw } /** A handler for text type responses */ @@ -141,6 +137,21 @@ const ShowPDF = (response: IRawResponse) => { return ShowUnknown(response) } +/** A handler for responses that cannot be parsed */ +const ShowRaw = (response: IRawResponse) => ( + <> + {ShowUnknown(response)} + + The response body could not be parsed. Displaying raw data. + + + +) + interface Responder { /** A label indicating the supported MIME type(s) */ label: string @@ -213,3 +224,9 @@ export const pickResponseHandler = (response: IRawResponse) => { } return result } + +export const fallbackResponseHandler = (): Responder => ({ + label: 'unknown', + isRecognized: (contentType: string) => !!contentType, + component: (response) => ShowRaw(response), +}) diff --git a/packages/run-it/src/test-data/index.ts b/packages/run-it/src/test-data/index.ts index 759a45006..402b1ff05 100644 --- a/packages/run-it/src/test-data/index.ts +++ b/packages/run-it/src/test-data/index.ts @@ -31,5 +31,6 @@ export { testHtmlResponse, testUnknownResponse, testErrorResponse, + testBogusJsonResponse, } from './responses' export { api } from './specs' diff --git a/packages/run-it/src/test-data/responses.ts b/packages/run-it/src/test-data/responses.ts index 2977e0eca..52aafd536 100644 --- a/packages/run-it/src/test-data/responses.ts +++ b/packages/run-it/src/test-data/responses.ts @@ -106,3 +106,13 @@ export const testErrorResponse: IRawResponse = { statusCode: 404, statusMessage: 'some status message', } + +export const testBogusJsonResponse: IRawResponse = { + url: 'https://some/json/data', + headers: {}, + contentType: 'application/json', + ok: true, + statusCode: 200, + statusMessage: 'OK', + body: Buffer.from('I AM A LYING JSON RESPONSE'), +}