diff --git a/changelogs/fragments/8839.yml b/changelogs/fragments/8839.yml new file mode 100644 index 000000000000..27477e376254 --- /dev/null +++ b/changelogs/fragments/8839.yml @@ -0,0 +1,2 @@ +fix: +- Fix a typo while inspecting values for large numerals in OSD and the JS client ([#8839](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8839)) \ No newline at end of file diff --git a/packages/osd-std/src/json.test.ts b/packages/osd-std/src/json.test.ts index 33abd71d91d2..0d4b900e0ca5 100644 --- a/packages/osd-std/src/json.test.ts +++ b/packages/osd-std/src/json.test.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import JSON11 from 'json11'; import { stringify, parse } from './json'; describe('json', () => { @@ -90,9 +91,55 @@ describe('json', () => { expect(stringify(input, replacer, 2)).toEqual(JSON.stringify(input, replacer, 2)); }); - it('can handle long numerals while parsing', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; + it('can handle positive long numerals while parsing', () => { + const longPositiveA = BigInt(Number.MAX_SAFE_INTEGER) * 2n; + const longPositiveB = BigInt(Number.MAX_SAFE_INTEGER) * 2n + 1n; + const text = + `{` + + // The space before and after the values, and the lack of spaces before comma are intentional + `"\\":${longPositiveA}": "[ ${longPositiveB.toString()}, ${longPositiveA.toString()} ]", ` + + `"positive": ${longPositiveA.toString()}, ` + + `"array": [ ${longPositiveB.toString()}, ${longPositiveA.toString()} ], ` + + `"negative": ${longPositiveB.toString()},` + + `"number": 102931203123987` + + `}`; + + const result = parse(text); + expect(result.positive).toBe(longPositiveA); + expect(result.negative).toBe(longPositiveB); + expect(result.array).toEqual([longPositiveB, longPositiveA]); + expect(result['":' + longPositiveA]).toBe( + `[ ${longPositiveB.toString()}, ${longPositiveA.toString()} ]` + ); + expect(result.number).toBe(102931203123987); + }); + + it('can handle negative long numerals while parsing', () => { + const longNegativeA = BigInt(Number.MIN_SAFE_INTEGER) * 2n; + const longNegativeB = BigInt(Number.MIN_SAFE_INTEGER) * 2n - 1n; + const text = + `{` + + // The space before and after the values, and the lack of spaces before comma are intentional + `"\\":${longNegativeA}": "[ ${longNegativeB.toString()}, ${longNegativeA.toString()} ]", ` + + `"positive": ${longNegativeA.toString()}, ` + + `"array": [ ${longNegativeB.toString()}, ${longNegativeA.toString()} ], ` + + `"negative": ${longNegativeB.toString()},` + + `"number": 102931203123987` + + `}`; + + const result = parse(text); + expect(result.positive).toBe(longNegativeA); + expect(result.negative).toBe(longNegativeB); + expect(result.array).toEqual([longNegativeB, longNegativeA]); + expect(result['":' + longNegativeA]).toBe( + `[ ${longNegativeB.toString()}, ${longNegativeA.toString()} ]` + ); + expect(result.number).toBe(102931203123987); + }); + + it('can handle mixed long numerals while parsing', () => { + const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n + 1n; + const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n - 1n; const text = `{` + // The space before and after the values, and the lack of spaces before comma are intentional @@ -113,6 +160,37 @@ describe('json', () => { expect(result.number).toBe(102931203123987); }); + it('does not use JSON11 when not needed', () => { + const spyParse = jest.spyOn(JSON11, 'parse'); + + const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n + 1n; + const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n - 1n; + const text = + `{` + + `"\\":${longPositive}": "[ ${longNegative.toString()}, ${longPositive.toString()} ]", ` + + `"number": 102931203123987` + + `}`; + parse(text); + + expect(spyParse).not.toHaveBeenCalled(); + }); + + it('uses JSON11 when dealing with long numerals', () => { + const spyParse = jest.spyOn(JSON11, 'parse'); + + const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n + 1n; + const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n - 1n; + const text = + `{` + + `"\\":${longPositive}": "[ ${longNegative.toString()}, ${longPositive.toString()} ]", ` + + `"positive": ${longPositive.toString()}, ` + + `"number": 102931203123987` + + `}`; + parse(text); + + expect(spyParse).toHaveBeenCalled(); + }); + it('can handle BigInt values while stringifying', () => { const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; diff --git a/packages/osd-std/src/json.ts b/packages/osd-std/src/json.ts index 4dcd3eb03e65..79a148f625f7 100644 --- a/packages/osd-std/src/json.ts +++ b/packages/osd-std/src/json.ts @@ -69,7 +69,7 @@ export const parse = ( numeralsAreNumbers && typeof val === 'number' && isFinite(val) && - (val < Number.MAX_SAFE_INTEGER || val > Number.MAX_SAFE_INTEGER) + (val < Number.MIN_SAFE_INTEGER || val > Number.MAX_SAFE_INTEGER) ) { numeralsAreNumbers = false; } diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 93f168c164b2..a26a9cfd075e 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -84,6 +84,15 @@ const run = async () => { }, ]) ); + //ToDo: Remove when opensearch-js is released to include https://github.com/opensearch-project/opensearch-js/pull/889 + promises.push( + patchFile('node_modules/@opensearch-project/opensearch-next/lib/Serializer.js', [ + { + from: 'val < Number.MAX_SAFE_INTEGER', + to: 'val < Number.MIN_SAFE_INTEGER', + }, + ]) + ); //Axios's type definition is far too advanced for OSD promises.push(