diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 92474fdbf456e..1782a251504e1 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -184,7 +184,7 @@ "@types/jest": "^26.0.3", "@types/jquery": "^3.5.8", "@types/js-levenshtein": "^1.1.0", - "@types/json-bigint": "^1.0.0", + "@types/json-bigint": "^1.0.1", "@types/react": "^16.9.43", "@types/react-dom": "^16.9.8", "@types/react-gravatar": "^2.6.8", @@ -18200,10 +18200,9 @@ "dev": true }, "node_modules/@types/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-WW+0cfH3ovFN6ROV+p/Xfw36dT6s16hbXBYIG49PYw6+j6e+AkpqYccctgxwyicBmC8CZDBnPhOH94shFhXgHQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.1.tgz", + "integrity": "sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw==" }, "node_modules/@types/json-schema": { "version": "7.0.9", @@ -54794,6 +54793,7 @@ "@types/d3-time-format": "^2.1.0", "@types/enzyme": "^3.10.5", "@types/fetch-mock": "^7.3.3", + "@types/json-bigint": "^1.0.1", "@types/lodash": "^4.14.149", "@types/math-expression-evaluator": "^1.2.1", "@types/node": "^18.0.0", @@ -69195,6 +69195,7 @@ "@types/d3-time-format": "^2.1.0", "@types/enzyme": "^3.10.5", "@types/fetch-mock": "^7.3.3", + "@types/json-bigint": "^1.0.1", "@types/lodash": "^4.14.149", "@types/math-expression-evaluator": "^1.2.1", "@types/node": "^18.0.0", @@ -71074,10 +71075,9 @@ "dev": true }, "@types/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-WW+0cfH3ovFN6ROV+p/Xfw36dT6s16hbXBYIG49PYw6+j6e+AkpqYccctgxwyicBmC8CZDBnPhOH94shFhXgHQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/json-bigint/-/json-bigint-1.0.1.tgz", + "integrity": "sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw==" }, "@types/json-schema": { "version": "7.0.9", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index b6a44c8bb6779..5d09c502d2142 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -248,7 +248,7 @@ "@types/jest": "^26.0.3", "@types/jquery": "^3.5.8", "@types/js-levenshtein": "^1.1.0", - "@types/json-bigint": "^1.0.0", + "@types/json-bigint": "^1.0.1", "@types/react": "^16.9.43", "@types/react-dom": "^16.9.8", "@types/react-gravatar": "^2.6.8", diff --git a/superset-frontend/packages/superset-ui-core/package.json b/superset-frontend/packages/superset-ui-core/package.json index 1958fe1a49fae..7faa531a67cd3 100644 --- a/superset-frontend/packages/superset-ui-core/package.json +++ b/superset-frontend/packages/superset-ui-core/package.json @@ -27,9 +27,9 @@ }, "devDependencies": { "@emotion/styled": "^11.3.0", - "resize-observer-polyfill": "1.5.1", "fetch-mock": "^6.5.2", - "jest-mock-console": "^1.0.0" + "jest-mock-console": "^1.0.0", + "resize-observer-polyfill": "1.5.1" }, "dependencies": { "@babel/runtime": "^7.1.2", @@ -38,14 +38,15 @@ "@types/d3-scale": "^2.1.1", "@types/d3-time": "^3.0.0", "@types/d3-time-format": "^2.1.0", + "@types/enzyme": "^3.10.5", + "@types/fetch-mock": "^7.3.3", + "@types/json-bigint": "^1.0.1", "@types/lodash": "^4.14.149", "@types/math-expression-evaluator": "^1.2.1", "@types/node": "^18.0.0", + "@types/prop-types": "^15.7.2", "@types/rison": "0.0.6", "@types/seedrandom": "^2.4.28", - "@types/fetch-mock": "^7.3.3", - "@types/enzyme": "^3.10.5", - "@types/prop-types": "^15.7.2", "@vx/responsive": "^0.0.199", "csstype": "^2.6.4", "d3-format": "^1.3.2", @@ -72,8 +73,8 @@ "@types/react": "*", "@types/react-loadable": "*", "@types/tinycolor2": "*", - "tinycolor2": "*", "react": "^16.13.1", - "react-loadable": "^5.5.0" + "react-loadable": "^5.5.0", + "tinycolor2": "*" } } diff --git a/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts b/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts index a0b9f149113a4..e0e5e0ebbe232 100644 --- a/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts +++ b/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import JSONbig from 'json-bigint'; import { ParseMethod, TextResponse, JsonResponse } from '../types'; @@ -25,7 +26,7 @@ export default async function parseResponse( ) { type ReturnType = T extends 'raw' | null ? Response - : T extends 'json' | undefined + : T extends 'json' | 'json-bigint' | undefined ? JsonResponse : T extends 'text' ? TextResponse @@ -46,6 +47,15 @@ export default async function parseResponse( }; return result as ReturnType; } + if (parseMethod === 'json-bigint') { + const rawData = await response.text(); + const json = JSONbig.parse(rawData); + const result: JsonResponse = { + response, + json, + }; + return result as ReturnType; + } // by default treat this as json if (parseMethod === undefined || parseMethod === 'json') { const json = await response.json(); @@ -56,6 +66,6 @@ export default async function parseResponse( return result as ReturnType; } throw new Error( - `Expected parseResponse=json|text|raw|null, got '${parseMethod}'.`, + `Expected parseResponse=json|json-bigint|text|raw|null, got '${parseMethod}'.`, ); } diff --git a/superset-frontend/packages/superset-ui-core/src/connection/types.ts b/superset-frontend/packages/superset-ui-core/src/connection/types.ts index 80b40da030c26..a63ffd8b68a08 100644 --- a/superset-frontend/packages/superset-ui-core/src/connection/types.ts +++ b/superset-frontend/packages/superset-ui-core/src/connection/types.ts @@ -70,7 +70,13 @@ export type Method = RequestInit['method']; export type Mode = RequestInit['mode']; export type Redirect = RequestInit['redirect']; export type ClientTimeout = number | undefined; -export type ParseMethod = 'json' | 'text' | 'raw' | null | undefined; +export type ParseMethod = + | 'json' + | 'json-bigint' + | 'text' + | 'raw' + | null + | undefined; export type Signal = RequestInit['signal']; export type Stringify = boolean; export type Url = string; diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts index d54be27e9c8f1..172038f1cc1d3 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts @@ -137,6 +137,21 @@ describe('parseResponse()', () => { expect(responseRaw.bodyUsed).toBe(false); }); + it('resolves to big number value if `parseMethod=json-bigint`', async () => { + const mockBigIntUrl = '/mock/get/bigInt'; + const mockGetBigIntPayload = + '{ "value": 9223372036854775807, "minusValue": -483729382918228373892 }'; + fetchMock.get(mockBigIntUrl, mockGetBigIntPayload); + const responseBigNumber = await parseResponse( + callApi({ url: mockBigIntUrl, method: 'GET' }), + 'json-bigint', + ); + expect(`${responseBigNumber.json.value}`).toEqual('9223372036854775807'); + expect(`${responseBigNumber.json.minusValue}`).toEqual( + '-483729382918228373892', + ); + }); + it('rejects if request.ok=false', async () => { expect.assertions(3); const mockNotOkayUrl = '/mock/notokay/url'; diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js b/superset-frontend/src/SqlLab/actions/sqlLab.js index 7811c0edd8d75..0da9295b3a02c 100644 --- a/superset-frontend/src/SqlLab/actions/sqlLab.js +++ b/superset-frontend/src/SqlLab/actions/sqlLab.js @@ -17,7 +17,6 @@ * under the License. */ import shortid from 'shortid'; -import JSONbig from 'json-bigint'; import { t, SupersetClient } from '@superset-ui/core'; import invert from 'lodash/invert'; import mapKeys from 'lodash/mapKeys'; @@ -306,12 +305,9 @@ export function fetchQueryResults(query, displayLimit) { return SupersetClient.get({ endpoint: `/superset/results/${query.resultsKey}/?rows=${displayLimit}`, - parseMethod: 'text', + parseMethod: 'json-bigint', }) - .then(({ text = '{}' }) => { - const bigIntJson = JSONbig.parse(text); - return dispatch(querySuccess(query, bigIntJson)); - }) + .then(({ json }) => dispatch(querySuccess(query, json))) .catch(response => getClientErrorObject(response).then(error => { const message = @@ -352,12 +348,11 @@ export function runQuery(query) { endpoint: `/superset/sql_json/${search}`, body: JSON.stringify(postPayload), headers: { 'Content-Type': 'application/json' }, - parseMethod: 'text', + parseMethod: 'json-bigint', }) - .then(({ text = '{}' }) => { + .then(({ json }) => { if (!query.runAsync) { - const bigIntJson = JSONbig.parse(text); - dispatch(querySuccess(query, bigIntJson)); + dispatch(querySuccess(query, json)); } }) .catch(response => diff --git a/superset-frontend/src/components/Chart/chartAction.js b/superset-frontend/src/components/Chart/chartAction.js index dea41497b82c6..f59c2da7199f2 100644 --- a/superset-frontend/src/components/Chart/chartAction.js +++ b/superset-frontend/src/components/Chart/chartAction.js @@ -139,6 +139,7 @@ const legacyChartDataRequest = async ( ...requestParams, url, postPayload: { form_data: formData }, + parseMethod: 'json-bigint', }; const clientMethod = @@ -196,6 +197,7 @@ const v1ChartDataRequest = async ( url, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), + parseMethod: 'json-bigint', }; return SupersetClient.post(querySettings); diff --git a/superset-frontend/src/components/Chart/chartActions.test.js b/superset-frontend/src/components/Chart/chartActions.test.js index 7c7af00a4b2e4..65b008de62f52 100644 --- a/superset-frontend/src/components/Chart/chartActions.test.js +++ b/superset-frontend/src/components/Chart/chartActions.test.js @@ -94,6 +94,25 @@ describe('chart actions', () => { ); expect(dispatch.args[0][0].type).toBe(actions.CHART_UPDATE_STARTED); }); + + it('should handle the bigint without regression', async () => { + getChartDataUriStub.restore(); + const mockBigIntUrl = '/mock/chart/data/bigint'; + const expectedBigNumber = '9223372036854775807'; + fetchMock.post(mockBigIntUrl, `{ "value": ${expectedBigNumber} }`, { + overwriteRoutes: true, + }); + getChartDataUriStub = sinon + .stub(exploreUtils, 'getChartDataUri') + .callsFake(() => URI(mockBigIntUrl)); + + const { json } = await actions.getChartDataRequest({ + formData: fakeMetadata, + }); + + expect(fetchMock.calls(mockBigIntUrl)).toHaveLength(1); + expect(json.value.toString()).toEqual(expectedBigNumber); + }); }); describe('legacy API', () => { @@ -194,5 +213,24 @@ describe('chart actions', () => { setupDefaultFetchMock(); }); }); + + it('should handle the bigint without regression', async () => { + getExploreUrlStub.restore(); + const mockBigIntUrl = '/mock/chart/data/bigint'; + const expectedBigNumber = '9223372036854775807'; + fetchMock.post(mockBigIntUrl, `{ "value": ${expectedBigNumber} }`, { + overwriteRoutes: true, + }); + getExploreUrlStub = sinon + .stub(exploreUtils, 'getExploreUrl') + .callsFake(() => mockBigIntUrl); + + const { json } = await actions.getChartDataRequest({ + formData: fakeMetadata, + }); + + expect(fetchMock.calls(mockBigIntUrl)).toHaveLength(1); + expect(json.result[0].value.toString()).toEqual(expectedBigNumber); + }); }); });