diff --git a/package.json b/package.json index cdacb6227531..2944fd9d768f 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ "@hapi/vision": "^6.1.0", "@hapi/wreck": "^17.1.0", "@opensearch-project/opensearch": "^1.1.0", - "@opensearch-project/opensearch-next": "npm:@opensearch-project/opensearch@^2.3.1", + "@opensearch-project/opensearch-next": "npm:@opensearch-project/opensearch@^2.2.0", "@osd/ace": "1.0.0", "@osd/analytics": "1.0.0", "@osd/apm-config-loader": "1.0.0", diff --git a/packages/osd-opensearch-archiver/package.json b/packages/osd-opensearch-archiver/package.json index 9f93b0a0e431..740c94bfd786 100644 --- a/packages/osd-opensearch-archiver/package.json +++ b/packages/osd-opensearch-archiver/package.json @@ -12,8 +12,7 @@ }, "dependencies": { "@osd/dev-utils": "1.0.0", - "@osd/std": "1.0.0", - "@opensearch-project/opensearch-next": "npm:@opensearch-project/opensearch@^2.3.1" + "@opensearch-project/opensearch": "^1.1.0" }, "devDependencies": {} } diff --git a/packages/osd-opensearch/package.json b/packages/osd-opensearch/package.json index c7263e1979a8..89e24f46dc43 100644 --- a/packages/osd-opensearch/package.json +++ b/packages/osd-opensearch/package.json @@ -12,7 +12,7 @@ "osd:watch": "../../scripts/use_node scripts/build --watch" }, "dependencies": { - "@opensearch-project/opensearch-next": "npm:@opensearch-project/opensearch@^2.3.1", + "@opensearch-project/opensearch": "^1.1.0", "@osd/dev-utils": "1.0.0", "abort-controller": "^3.0.0", "chalk": "^4.1.0", diff --git a/packages/osd-std/README.md b/packages/osd-std/README.md index 24f888979d25..3730735cb5a4 100644 --- a/packages/osd-std/README.md +++ b/packages/osd-std/README.md @@ -1,73 +1,3 @@ # `@osd/std` — OpenSearch Dashboards standard library -This package is a set of utilities that can be used both on server-side and client-side. - -## API - -#### `assertNever` - -Can be used in switch statements to ensure we perform exhaustive checks. - -#### `deepFreeze` - -Apply `Object.freeze` to a value recursively and convert the return type to `Readonly` variant recursively. - -#### `get` - -Retrieve the value for the specified path of an object. - -#### `getFlattenedObject` - -Flatten a deeply nested object to a map of dot-separated paths, pointing to all of its primitive values and arrays. - -#### `stringify` and `parse` - -Drop-in replacement for `JSON.stringify` and `JSON.parse`, capable of handling long numerals and `BigInt` values. - -#### `mapToObject` - -Convert a map to an object. - -#### `mapValuesOfMap` - -Create a new `Map` populated with the results of calling a provided function on every element in the input `Map`. - -#### `groupIntoMap` - -Group elements of an `Array` into a `Map` based on a provided function. - -#### `merge` - -Deeply merge two objects, omitting undefined values, and not deeply merging arrays. - -#### `pick` - -Create a new `Object` of specified keys and their values from an input `Object`. - -#### `withTimeout` - -Apply a `timeout` duration to a `Promise` before throwing an `Error` with the provided message. - -#### `firstValueFrom` and `lastValueFrom` - -Get a `Promise` that resolves as soon as the first or last value arrives from an observable. - -#### `unset` - -Unset a (potentially nested) key from given object. - -#### `modifyUrl` - -Get an `Object` resulting from applying a provided function to the meaningful parts of a URL. - -#### `isRelativeUrl` - -Determine if a url is relative. - -#### `getUrlOrigin` - -Get the origin URL of a provided URL. - -#### `validateObject` - -Deeply validate that an `Object` does not contain any `__proto__` or `constructor.prototype` keys, or circular references. \ No newline at end of file +This package is a set of utilities that can be used both on server-side and client-side. \ No newline at end of file diff --git a/packages/osd-std/src/__snapshots__/json.test.ts.snap b/packages/osd-std/src/__snapshots__/json.test.ts.snap deleted file mode 100644 index 42854db1dd78..000000000000 --- a/packages/osd-std/src/__snapshots__/json.test.ts.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`json can apply a replacer and spaces values while stringifying BigInts 1`] = ` -"{ - \\"\\\\\\": 18014398509481982\\": \\"\\", - \\"positive\\": 54043195528445946, - \\"negative\\": -54043195528445946, - \\"array\\": [ - -54043195528445946, - 54043195528445946, - [ - \\"]]>\\" - ] - ], - \\"number\\": \\"5d9d89cc6b13\\" -}" -`; - -exports[`json can handle BigInt values while stringifying 1`] = `"{\\"\\\\\\": 18014398509481982\\":\\"[ -18014398509481982, 18014398509481982 ]\\",\\"positive\\":18014398509481982,\\"negative\\":-18014398509481982,\\"array\\":[-18014398509481982,18014398509481982],\\"number\\":102931203123987}"`; diff --git a/packages/osd-std/src/index.ts b/packages/osd-std/src/index.ts index 170c819a2b0a..0b3c65d8cc04 100644 --- a/packages/osd-std/src/index.ts +++ b/packages/osd-std/src/index.ts @@ -42,4 +42,3 @@ export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; export { validateObject } from './validate_object'; export * from './rxjs_7'; -export { parse, stringify } from './json'; diff --git a/packages/osd-std/src/json.test.ts b/packages/osd-std/src/json.test.ts deleted file mode 100644 index 33abd71d91d2..000000000000 --- a/packages/osd-std/src/json.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { stringify, parse } from './json'; - -describe('json', () => { - it('can parse', () => { - const input = { - a: [ - { A: 1 }, - { B: '2' }, - { C: [1, 2, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'] }, - ], - b: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - c: { - i: {}, - ii: [], - iii: '', - iv: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - }, - }; - const result = parse(JSON.stringify(input)); - expect(result).toEqual(input); - }); - - it('can stringify', () => { - const input = { - a: [ - { A: 1 }, - { B: '2' }, - { C: [1, 2, 3, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'] }, - ], - b: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - c: { - i: {}, - ii: [], - iii: '', - iv: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', - }, - }; - const result = stringify(input); - expect(result).toEqual(JSON.stringify(input)); - }); - - it('can apply a reviver while parsing', () => { - const input = { - A: 255, - B: { - i: [[]], - ii: 'Lorem ipsum', - iii: {}, - rand: Math.random(), - }, - }; - const text = JSON.stringify(input); - function reviver(this: any, key: string, val: any) { - if (Array.isArray(val) && toString.call(this) === '[object Object]') this._hasArrays = true; - else if (typeof val === 'string') val = ``; - else if (typeof val === 'number') val = val.toString(16); - else if (toString.call(this) === '[object Object]' && key === 'rand' && val === input.B.rand) - this._found = true; - return val; - } - - expect(parse(text, reviver)).toEqual(JSON.parse(text, reviver)); - }); - - it('can apply a replacer and spaces while stringifying', () => { - const input = { - A: 255, - B: { - i: [[]], - ii: 'Lorem ipsum', - iii: {}, - rand: Math.random(), - }, - }; - - function replacer(this: any, key: string, val: any) { - if (Array.isArray(val) && val.length === 0) val.push(''); - else if (typeof val === 'string') val = ``; - else if (typeof val === 'number') val = val.toString(16); - else if (toString.call(this) === '[object Object]' && key === 'rand' && val === input.B.rand) - val = 1; - return val; - } - - 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; - const text = - `{` + - // The space before and after the values, and the lack of spaces before comma are intentional - `"\\":${longPositive}": "[ ${longNegative.toString()}, ${longPositive.toString()} ]", ` + - `"positive": ${longPositive.toString()}, ` + - `"array": [ ${longNegative.toString()}, ${longPositive.toString()} ], ` + - `"negative": ${longNegative.toString()},` + - `"number": 102931203123987` + - `}`; - - const result = parse(text); - expect(result.positive).toBe(longPositive); - expect(result.negative).toBe(longNegative); - expect(result.array).toEqual([longNegative, longPositive]); - expect(result['":' + longPositive]).toBe( - `[ ${longNegative.toString()}, ${longPositive.toString()} ]` - ); - expect(result.number).toBe(102931203123987); - }); - - it('can handle BigInt values while stringifying', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - const input = { - [`": ${longPositive}`]: `[ ${longNegative.toString()}, ${longPositive.toString()} ]`, - positive: longPositive, - negative: longNegative, - array: [longNegative, longPositive], - number: 102931203123987, - }; - - expect(stringify(input)).toMatchSnapshot(); - }); - - it('can apply a reviver on long numerals while parsing', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - const text = - `{` + - // The space before and after the values, and the lack of spaces before comma are intentional - `"\\":${longPositive}": "[ ${longNegative.toString()}, ${longPositive.toString()} ]", ` + - `"positive": ${longPositive.toString()}, ` + - `"array": [ ${longNegative.toString()}, ${longPositive.toString()} ], ` + - `"negative": ${longNegative.toString()},` + - `"number": 102931203123987` + - `}`; - - const reviver = (key: string, val: any) => (typeof val === 'bigint' ? val * 3n : val); - - const result = parse(text, reviver); - expect(result.positive).toBe(longPositive * 3n); - expect(result.negative).toBe(longNegative * 3n); - expect(result.array).toEqual([longNegative * 3n, longPositive * 3n]); - expect(result['":' + longPositive]).toBe( - `[ ${longNegative.toString()}, ${longPositive.toString()} ]` - ); - expect(result.number).toBe(102931203123987); - }); - - it('can apply a replacer and spaces values while stringifying BigInts', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - const input = { - [`": ${longPositive}`]: `[ ${longNegative.toString()}, ${longPositive.toString()} ]`, - positive: longPositive, - negative: longNegative, - array: [longNegative, longPositive, []], - number: 102931203123987, - }; - - function replacer(this: any, key: string, val: any) { - if (typeof val === 'bigint') val = val * 3n; - else if (Array.isArray(val) && val.length === 0) val.push(''); - else if (typeof val === 'string') val = ``; - else if (typeof val === 'number') val = val.toString(16); - return val; - } - - expect(stringify(input, replacer, 4)).toMatchSnapshot(); - }); -}); diff --git a/packages/osd-std/src/json.ts b/packages/osd-std/src/json.ts deleted file mode 100644 index 7c619dcd1656..000000000000 --- a/packages/osd-std/src/json.ts +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -/* In JavaScript, a `Number` is a 64-bit floating-point value which can store 16 digits. However, the - * serializer and deserializer will need to cater to numeric values generated by other languages which - * can have up to 19 digits. Native JSON parser and stringifier, incapable of handling the extra - * digits, corrupt the values, making them unusable. - * - * To work around this limitation, the deserializer converts long sequences of digits into strings and - * marks them before applying the parser. During the parsing, string values that begin with the mark - * are converted to `BigInt` values. - * Similarly, during stringification, the serializer converts `BigInt` values to marked strings and - * when done, it replaces them with plain numerals. - * - * `Number.MAX_SAFE_INTEGER`, 9,007,199,254,740,991, is the largest number that the native methods can - * parse and stringify, and any numeral greater than that would need to be translated using the - * workaround; all 17-digits or longer and only tail-end of the 16-digits need translation. It would - * be unfair to all the 16-digit numbers if the translation applied to `\d{16,}` only to cover the - * less than 10%. Hence, a RegExp is created to only match numerals too long to be a number. - * - * To make the explanation simpler, let's assume that MAX_SAFE_INTEGER is 8921 which has 4 digits. - * Starting from the right, we take each digit onwards, `[-9]`: - * 1) 7922 - 7929: 792[2-9]\d{0} - * 2) 7930 - 7999: 79[3-9]\d{1} - * 9) 9 + 1 = 10 which results in a rollover; no need to do anything. - * 8) 9000 - 9999: [9-9]\d{3} - * Finally we add anything 5 digits or longer: `\d{5,} - * - * Note: A better solution would use AST but considering its performance penalty, RegExp is the next - * best thing. - */ -const maxIntAsString = String(Number.MAX_SAFE_INTEGER); -const maxIntLength = maxIntAsString.length; -// Sub-patterns for each digit -const longNumeralMatcherTokens = [`\\d{${maxIntAsString.length + 1},}`]; -for (let i = 0; i < maxIntLength; i++) { - if (maxIntAsString[i] !== '9') { - longNumeralMatcherTokens.push( - maxIntAsString.substring(0, i) + - `[${parseInt(maxIntAsString[i], 10) + 1}-9]` + - `\\d{${maxIntLength - i - 1}}` - ); - } -} - -/* The matcher that looks for `": , ...}` and `[..., , ...]` - * - * The pattern starts by looking for `":` not immediately preceded by a `\`. That should be - * followed by any of the numeric sub-patterns. A comma, end of an array, end of an object, or - * the end of the input are the only acceptable elements after it. - * - * Note: This RegExp can result in false-positive hits on the likes of `{"key": "[ ]"}` and - * those are cleaned out during parsing. - */ -const longNumeralMatcher = new RegExp( - `((?:\\[|,|(? { - // coverage:ignore-line - if (!length || length < 0) return []; - const choices = []; - const arr = markerChars; - const arrLength = arr.length; - const temp = Array(length); - - (function fill(pos, start) { - if (pos === length) return choices.push(temp.join('')); - - for (let i = start; i < arrLength; i++) { - temp[pos] = arr[i]; - fill(pos + 1, i); - } - })(0, 0); - - return choices; -}; - -/* Experiments with different combinations of various lengths, until one is found to not be in - * the input string. - */ -const getMarker = (text: string): { marker: string; length: number } => { - let marker; - let length = 0; - do { - length++; - getMarkerChoices(length).some((markerChoice) => { - if (text.indexOf(markerChoice) === -1) { - marker = markerChoice; - return true; - } - }); - } while (!marker); - - return { - marker, - length, - }; -}; - -const parseStringWithLongNumerals = ( - text: string, - reviver?: ((this: any, key: string, value: any) => any) | null -): any => { - const { marker, length } = getMarker(text); - - let hadException; - let obj; - let markedJSON = text.replace(longNumeralMatcher, `$1"${marker}$2"$3`); - const markedValueMatcher = new RegExp(`^${marker}-?\\d+$`); - - /* Convert marked values to BigInt values. - * The `startsWith` is purely for performance, to avoid running `test` if not needed. - */ - const convertMarkedValues = (val: any) => - typeof val === 'string' && val.startsWith(marker) && markedValueMatcher.test(val) - ? BigInt(val.substring(length)) - : val; - - /* For better performance, instead of testing for existence of `reviver` on each value, two almost - * identical functions are used. - */ - const parseMarkedText = reviver - ? (markedText: string) => - JSON.parse(markedText, function (key, val) { - return reviver.call(this, key, convertMarkedValues(val)); - }) - : (markedText: string) => JSON.parse(markedText, (key, val) => convertMarkedValues(val)); - - /* RegExp cannot replace AST and the process of marking adds quotes. So, any false-positive hit - * will make the JSON string unparseable. - * - * To find those instances, we try to parse and watch for the location of any errors. If an error - * is caused by the marking, we remove that single marking and try again. - */ - do { - try { - hadException = false; - obj = parseMarkedText(markedJSON); - } catch (e) { - hadException = true; - /* There are two types of exception objects that can be raised: - * 1) a proper object with lineNumber and columnNumber which we can use - * 2) a textual message with the position that we need to parse - */ - let { lineNumber, columnNumber } = e; - if (!lineNumber || !columnNumber) { - const match = e?.message?.match?.(/^Unexpected token.*at position (\d+)$/); - if (match) { - lineNumber = 1; - // The position is zero-indexed; adding 1 to normalize it for the -2 that comes later - columnNumber = parseInt(match[1], 10) + 1; - } - } - - if (lineNumber < 1 || columnNumber < 2) { - /* The problem is not with this replacement. - * Note: This will never happen because the outer parse would have already thrown. - */ - // coverage:ignore-line - throw e; - } - - /* We need to skip e.lineNumber - 1 number of `\n` occurrences. - * Then, we need to go to e.columnNumber - 2 to look for `"\d+"`; we need to `-1` to - * account for the quote but an additional `-1` is needed because columnNumber starts from 1. - */ - const re = new RegExp( - `^((?:.*\\n){${lineNumber - 1}}[^\\n]{${columnNumber - 2}})"${marker}(-?\\d+)"` - ); - if (!re.test(markedJSON)) { - /* The exception is not caused by adding the marker. - * Note: This will never happen because the outer parse would have already thrown. - */ - // coverage:ignore-line - throw e; - } - - // We have found a bad replacement; let's remove it. - markedJSON = markedJSON.replace(re, '$1$2'); - } - } while (hadException); - - return obj; -}; - -const stringifyObjectWithBigInts = ( - obj: any, - candidate: string, - replacer?: ((this: any, key: string, value: any) => any) | null, - space?: string | number -): string => { - const { marker } = getMarker(candidate); - - /* The matcher that looks for "" - * Because we have made sure that `marker` was never present in the original object, we can - * carelessly assume every "" is due to our marking. - */ - const markedBigIntMatcher = new RegExp(`"${marker}(-?\\d+)"`, 'g'); - - /* Convert BigInt values to a string and mark them. - * Can't be bothered with Number values outside the safe range because they are already corrupted. - * - * For better performance, instead of testing for existence of `replacer` on each value, two almost - * identical functions are used. - */ - const addMarkerToBigInts = replacer - ? function (this: any, key: string, val: any) { - // replacer is called before marking because marking changes the type - const newVal = replacer.call(this, key, val); - return typeof newVal === 'bigint' ? `${marker}${newVal.toString()}` : newVal; - } - : (key: string, val: any) => (typeof val === 'bigint' ? `${marker}${val.toString()}` : val); - - return ( - JSON.stringify(obj, addMarkerToBigInts, space) - // Replace marked substrings with just the numerals - .replace(markedBigIntMatcher, '$1') - ); -}; - -export const stringify = ( - obj: any, - replacer?: ((this: any, key: string, value: any) => any) | null, - space?: string | number -): string => { - let text; - let numeralsAreNumbers = true; - /* For better performance, instead of testing for existence of `replacer` on each value, two almost - * identical functions are used. - * - * Note: Converting BigInt values to numbers, `Number()` is much faster that `parseInt()`. Since we - * check the `type`, it is safe to just use `Number()`. - */ - const checkForBigInts = replacer - ? function (this: any, key: string, val: any) { - if (typeof val === 'bigint') { - numeralsAreNumbers = false; - return replacer.call(this, key, Number(val)); - } - return replacer.call(this, key, val); - } - : (key: string, val: any) => { - if (typeof val === 'bigint') { - numeralsAreNumbers = false; - return Number(val); - } - return val; - }; - - /* While this is a check for possibly having BigInt values, if none were found, the results is - * sufficient to fulfill the purpose of the function. However, if BigInt values were found, we will - * use `stringifyObjectWithBigInts` to do this again. - * - * The goal was not to punish every object that doesn't have a BigInt with the more expensive - * `stringifyObjectWithBigInts`. Those with BigInt values are also not unduly burdened because we - * still need it in its string form to find a suitable marker. - */ - text = JSON.stringify(obj, checkForBigInts, space); - - if (!numeralsAreNumbers) { - text = stringifyObjectWithBigInts(obj, text, replacer, space); - } - - return text; -}; - -export const parse = ( - text: string, - reviver?: ((this: any, key: string, value: any) => any) | null -): any => { - let obj; - let numeralsAreNumbers = true; - const inspectValueForLargeNumerals = (val: any) => { - if ( - numeralsAreNumbers && - typeof val === 'number' && - (val < Number.MAX_SAFE_INTEGER || val > Number.MAX_SAFE_INTEGER) - ) { - numeralsAreNumbers = false; - } - - // This function didn't have to have a return value but having it makes the rest cleaner - return val; - }; - - /* For better performance, instead of testing for existence of `reviver` on each value, two almost - * identical functions are used. - */ - const checkForLargeNumerals = reviver - ? function (this: any, key: string, val: any) { - return inspectValueForLargeNumerals(reviver.call(this, key, val)); - } - : (key: string, val: any) => inspectValueForLargeNumerals(val); - - /* While this is a check for possibly having BigInt values, if none were found, the results is - * sufficient to fulfill the purpose of the function. However, if BigInt values were found, we will - * use `stringifyObjectWithBigInts` to do this again. - * - * The goal was not to punish every object that doesn't have a BigInt with the more expensive - * `stringifyObjectWithBigInts`. Those with BigInt values are also not unduly burdened because we - * still need it in its string form to find a suitable marker. - */ - obj = JSON.parse(text, checkForLargeNumerals); - - if (!numeralsAreNumbers) { - obj = parseStringWithLongNumerals(text, reviver); - } - - return obj; -}; diff --git a/src/core/public/http/fetch.test.ts b/src/core/public/http/fetch.test.ts index 20f070dbba80..efc8d4aa31bb 100644 --- a/src/core/public/http/fetch.test.ts +++ b/src/core/public/http/fetch.test.ts @@ -825,39 +825,4 @@ describe('Fetch', () => { expect(usedSpy).toHaveBeenCalledTimes(2); }); }); - - describe('long numerals', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - - it('should use alternate parser on JSON responses when asked to', async () => { - fetchMock.get('*', { - body: `{"long-max": ${longPositive}, "long-min": ${longNegative}}`, - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); - - await expect(fetchInstance.fetch('/my/path', { withLongNumerals: true })).resolves.toEqual({ - 'long-max': longPositive, - 'long-min': longNegative, - }); - }); - - it('should use alternate parser on non-JSON responses when asked to', async () => { - fetchMock.get('*', { - body: `{"long-max": ${longPositive}, "long-min": ${longNegative}}`, - status: 200, - headers: { - 'Content-Type': 'text', - }, - }); - - await expect(fetchInstance.fetch('/my/path', { withLongNumerals: true })).resolves.toEqual({ - 'long-max': longPositive, - 'long-min': longNegative, - }); - }); - }); }); diff --git a/src/core/public/http/fetch.ts b/src/core/public/http/fetch.ts index 767d58643003..694372c46d99 100644 --- a/src/core/public/http/fetch.ts +++ b/src/core/public/http/fetch.ts @@ -31,7 +31,7 @@ import { omitBy } from 'lodash'; import { format } from 'url'; import { BehaviorSubject } from 'rxjs'; -import { isRelativeUrl, parse } from '@osd/std'; +import { isRelativeUrl } from '@osd/std'; import { IBasePath, @@ -190,12 +190,12 @@ export class Fetch { if (NDJSON_CONTENT.test(contentType)) { body = await response.blob(); } else if (JSON_CONTENT.test(contentType)) { - body = fetchOptions.withLongNumerals ? parse(await response.text()) : await response.json(); + body = await response.json(); } else { const text = await response.text(); try { - body = fetchOptions.withLongNumerals ? parse(text) : JSON.parse(text); + body = JSON.parse(text); } catch (err) { body = text; } diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts index 3b7dff71c811..ab046e6d2d5a 100644 --- a/src/core/public/http/types.ts +++ b/src/core/public/http/types.ts @@ -257,12 +257,6 @@ export interface HttpFetchOptions extends HttpRequestInit { * response information. When `false`, the return type will just be the parsed response body. Defaults to `false`. */ asResponse?: boolean; - - /** - * When `true`, if the response has a JSON mime type, the {@link HttpResponse} will use an alternate JSON parser - * that converts long numerals to BigInts. Defaults to `false`. - */ - withLongNumerals?: boolean; } /** diff --git a/src/core/server/opensearch/client/cluster_client.test.ts b/src/core/server/opensearch/client/cluster_client.test.ts index 81f55b987805..1510d2b148fe 100644 --- a/src/core/server/opensearch/client/cluster_client.test.ts +++ b/src/core/server/opensearch/client/cluster_client.test.ts @@ -99,16 +99,8 @@ describe('ClusterClient', () => { const scopedClusterClient = clusterClient.asScoped(request); - const expected = { headers: expect.any(Object) }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: expect.any(Object) }); expect(scopedClusterClient.asInternalUser).toBe(clusterClient.asInternalUser); expect(scopedClusterClient.asCurrentUser).toBe(scopedClient.child.mock.results[0].value); @@ -121,7 +113,7 @@ describe('ClusterClient', () => { const scopedClusterClient1 = clusterClient.asScoped(request); const scopedClusterClient2 = clusterClient.asScoped(request); - expect(scopedClient.child).toHaveBeenCalledTimes(2 * 2); + expect(scopedClient.child).toHaveBeenCalledTimes(2); expect(scopedClusterClient1).not.toBe(scopedClusterClient2); expect(scopedClusterClient1.asInternalUser).toBe(scopedClusterClient2.asInternalUser); @@ -143,18 +135,10 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, foo: 'bar', 'x-opaque-id': expect.any(String) }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('creates a scoped facade with filtered auth headers', () => { @@ -171,18 +155,10 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, authorization: 'auth', 'x-opaque-id': expect.any(String) }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respects auth headers precedence', () => { @@ -203,18 +179,10 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, authorization: 'auth', 'x-opaque-id': expect.any(String) }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('includes the `customHeaders` from the config without filtering them', () => { @@ -232,23 +200,15 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, foo: 'bar', hello: 'dolly', 'x-opaque-id': expect.any(String), }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('adds the x-opaque-id header based on the request id', () => { @@ -265,21 +225,13 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, 'x-opaque-id': 'my-fake-id', }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respect the precedence of auth headers over config headers', () => { @@ -299,23 +251,15 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, foo: 'auth', hello: 'dolly', 'x-opaque-id': expect.any(String), }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respect the precedence of request headers over config headers', () => { @@ -335,23 +279,15 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, foo: 'request', hello: 'dolly', 'x-opaque-id': expect.any(String), }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respect the precedence of config headers over default headers', () => { @@ -368,21 +304,13 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { [headerKey]: 'foo', 'x-opaque-id': expect.any(String), }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respect the precedence of request headers over default headers', () => { @@ -399,21 +327,13 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { [headerKey]: 'foo', 'x-opaque-id': expect.any(String), }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('respect the precedence of x-opaque-id header over config headers', () => { @@ -435,21 +355,13 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, 'x-opaque-id': 'from request', }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('filter headers when called with a `FakeRequest`', () => { @@ -468,18 +380,10 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, authorization: 'auth' }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); it('does not add auth headers when called with a `FakeRequest`', () => { @@ -500,18 +404,10 @@ describe('ClusterClient', () => { clusterClient.asScoped(request); - const expected = { + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith({ headers: { ...DEFAULT_HEADERS, foo: 'bar' }, - }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + }); }); }); diff --git a/src/core/server/opensearch/client/cluster_client.ts b/src/core/server/opensearch/client/cluster_client.ts index 8ea87bb910f7..ac2348921658 100644 --- a/src/core/server/opensearch/client/cluster_client.ts +++ b/src/core/server/opensearch/client/cluster_client.ts @@ -90,27 +90,10 @@ export class ClusterClient implements ICustomClusterClient { asScoped(request: ScopeableRequest) { const scopedHeaders = this.getScopedHeaders(request); - const scopedClient = this.rootScopedClient.child({ headers: scopedHeaders, }); - - const asInternalUserWithLongNumeralsSupport = this.asInternalUser.child({ - // @ts-expect-error - Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release - enableLongNumeralSupport: true, - }); - - const scopedClientWithLongNumeralsSupport = this.rootScopedClient.child({ - headers: scopedHeaders, - // @ts-expect-error - Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release - enableLongNumeralSupport: true, - }); - return new ScopedClusterClient( - this.asInternalUser, - scopedClient, - asInternalUserWithLongNumeralsSupport, - scopedClientWithLongNumeralsSupport - ); + return new ScopedClusterClient(this.asInternalUser, scopedClient); } public async close() { diff --git a/src/core/server/opensearch/client/mocks.ts b/src/core/server/opensearch/client/mocks.ts index 40b731f0f3bf..7b055d5b03cf 100644 --- a/src/core/server/opensearch/client/mocks.ts +++ b/src/core/server/opensearch/client/mocks.ts @@ -112,16 +112,12 @@ const createClientMock = (): OpenSearchClientMock => export interface ScopedClusterClientMock { asInternalUser: OpenSearchClientMock; asCurrentUser: OpenSearchClientMock; - asInternalUserWithLongNumeralsSupport: OpenSearchClientMock; - asCurrentUserWithLongNumeralsSupport: OpenSearchClientMock; } const createScopedClusterClientMock = () => { const mock: ScopedClusterClientMock = { asInternalUser: createClientMock(), asCurrentUser: createClientMock(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: createClientMock(), }; return mock; @@ -130,16 +126,12 @@ const createScopedClusterClientMock = () => { export interface ClusterClientMock { asInternalUser: OpenSearchClientMock; asScoped: jest.MockedFunction<() => ScopedClusterClientMock>; - asInternalUserWithLongNumeralsSupport: OpenSearchClientMock; - asCurrentUserWithLongNumeralsSupport: jest.MockedFunction<() => ScopedClusterClientMock>; } const createClusterClientMock = () => { const mock: ClusterClientMock = { asInternalUser: createClientMock(), asScoped: jest.fn(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: jest.fn(), }; mock.asScoped.mockReturnValue(createScopedClusterClientMock()); @@ -153,8 +145,6 @@ const createCustomClusterClientMock = () => { const mock: CustomClusterClientMock = { asInternalUser: createClientMock(), asScoped: jest.fn(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: jest.fn(), close: jest.fn(), }; diff --git a/src/core/server/opensearch/client/scoped_cluster_client.test.ts b/src/core/server/opensearch/client/scoped_cluster_client.test.ts index 5e3d222c3be2..396075d35316 100644 --- a/src/core/server/opensearch/client/scoped_cluster_client.test.ts +++ b/src/core/server/opensearch/client/scoped_cluster_client.test.ts @@ -36,12 +36,7 @@ describe('ScopedClusterClient', () => { const internalClient = opensearchClientMock.createOpenSearchClient(); const scopedClient = opensearchClientMock.createOpenSearchClient(); - const scopedClusterClient = new ScopedClusterClient( - internalClient, - scopedClient, - internalClient, - scopedClient - ); + const scopedClusterClient = new ScopedClusterClient(internalClient, scopedClient); expect(scopedClusterClient.asInternalUser).toBe(internalClient); }); @@ -50,12 +45,7 @@ describe('ScopedClusterClient', () => { const internalClient = opensearchClientMock.createOpenSearchClient(); const scopedClient = opensearchClientMock.createOpenSearchClient(); - const scopedClusterClient = new ScopedClusterClient( - internalClient, - scopedClient, - internalClient, - scopedClient - ); + const scopedClusterClient = new ScopedClusterClient(internalClient, scopedClient); expect(scopedClusterClient.asCurrentUser).toBe(scopedClient); }); diff --git a/src/core/server/opensearch/client/scoped_cluster_client.ts b/src/core/server/opensearch/client/scoped_cluster_client.ts index 4453243bd3ba..d4db3ce3606c 100644 --- a/src/core/server/opensearch/client/scoped_cluster_client.ts +++ b/src/core/server/opensearch/client/scoped_cluster_client.ts @@ -49,25 +49,12 @@ export interface IScopedClusterClient { * on behalf of the user that initiated the request to the OpenSearch Dashboards server. */ readonly asCurrentUser: OpenSearchClient; - /** - * A {@link OpenSearchClient | client}, with support for long numerals, to be used to - * query the opensearch cluster on behalf of the internal OpenSearch Dashboards user. - */ - readonly asInternalUserWithLongNumeralsSupport: OpenSearchClient; - /** - * A {@link OpenSearchClient | client}, with support for long numerals, to be used to - * query the opensearch cluster on behalf of the user that initiated the request to - * the OpenSearch Dashboards server. - */ - readonly asCurrentUserWithLongNumeralsSupport: OpenSearchClient; } /** @internal **/ export class ScopedClusterClient implements IScopedClusterClient { constructor( public readonly asInternalUser: OpenSearchClient, - public readonly asCurrentUser: OpenSearchClient, - public readonly asInternalUserWithLongNumeralsSupport: OpenSearchClient, - public readonly asCurrentUserWithLongNumeralsSupport: OpenSearchClient + public readonly asCurrentUser: OpenSearchClient ) {} } diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts index fbeadf4cc7de..eaa171132785 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts @@ -86,27 +86,6 @@ describe('test sendRequestToOpenSearch', () => { }); }); - it('test request success, json with long numerals', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - const mockHttpResponse = createMockHttpResponse( - 200, - 'ok', - [['Content-Type', 'application/json, utf-8']], - { - 'long-max': longPositive, - 'long-min': longNegative, - } - ); - - jest.spyOn(opensearch, 'send').mockResolvedValue(mockHttpResponse); - sendRequestToOpenSearch(dummyArgs).then((result) => { - const value = (result as any)[0].response.value; - expect(value).toMatch(new RegExp(`"long-max": ${longPositive}[,\n]`)); - expect(value).toMatch(new RegExp(`"long-min": ${longNegative}[,\n]`)); - }); - }); - it('test request success, text', () => { const mockHttpResponse = createMockHttpResponse( 200, diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.ts index 1cb992a7a99c..4e1ae7267542 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.ts @@ -28,7 +28,6 @@ * under the License. */ -import { stringify } from '@osd/std'; import { HttpFetchError, HttpSetup } from 'opensearch-dashboards/public'; import { extractDeprecationMessages } from '../../../lib/utils'; import { XJson } from '../../../../../opensearch_ui_shared/public'; @@ -117,7 +116,7 @@ export function sendRequestToOpenSearch( const contentType = httpResponse.response.headers.get('Content-Type') as BaseResponseType; let value = ''; if (contentType.includes('application/json')) { - value = stringify(httpResponse.body, null, 2); + value = JSON.stringify(httpResponse.body, null, 2); } else { value = httpResponse.body; } @@ -156,7 +155,7 @@ export function sendRequestToOpenSearch( if (httpError.body) { contentType = httpResponse.headers.get('Content-Type') as string; if (contentType?.includes('application/json')) { - value = stringify(httpError.body, null, 2); + value = JSON.stringify(httpError.body, null, 2); } else { value = httpError.body; } diff --git a/src/plugins/console/public/lib/opensearch/opensearch.ts b/src/plugins/console/public/lib/opensearch/opensearch.ts index d1ba8797e474..b0158945eb25 100644 --- a/src/plugins/console/public/lib/opensearch/opensearch.ts +++ b/src/plugins/console/public/lib/opensearch/opensearch.ts @@ -57,7 +57,6 @@ export async function send( body: data, prependBasePath: true, asResponse: true, - withLongNumerals: true, }); } diff --git a/src/plugins/console/public/lib/utils/__tests__/__snapshots__/utils.test.ts.snap b/src/plugins/console/public/lib/utils/__tests__/__snapshots__/utils.test.ts.snap deleted file mode 100644 index 48739d834c9a..000000000000 --- a/src/plugins/console/public/lib/utils/__tests__/__snapshots__/utils.test.ts.snap +++ /dev/null @@ -1,118 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Utils class formatRequestBodyDoc changed with indenting 1`] = ` -Object { - "changed": true, - "data": Array [ - "{ - \\"data\\": { - \\"long-max\\": 18014398509481982, - \\"long-min\\": -18014398509481982, - \\"num\\": 2 - } -}", - "{ - \\"'\\\\\\"key\\\\\\"'\\": \\"\\"\\"'oddly' 'quoted \\"value\\"'\\"\\"\\", - \\"obj\\": \\"\\"\\"{\\"nested\\": \\"inner-value\\", \\"inner-max\\": 18014398509481982\\"\\"\\" -}", - "{ - \\"version\\": true, - \\"size\\": 500, - \\"sort\\": [ - { - \\"time\\": { - \\"order\\": \\"desc\\", - \\"unmapped_type\\": \\"boolean\\" - } - } - ], - \\"aggs\\": { - \\"2\\": { - \\"date_histogram\\": { - \\"field\\": \\"time\\", - \\"fixed_interval\\": \\"30s\\", - \\"time_zone\\": \\"America/Los_Angeles\\", - \\"min_doc_count\\": 1 - } - } - }, - \\"_source\\": { - \\"excludes\\": [] - }, - \\"query\\": { - \\"bool\\": { - \\"filter\\": [ - { - \\"match_all\\": {} - }, - { - \\"range\\": { - \\"time\\": { - \\"gte\\": \\"2222-22-22T22:22:22.222Z\\", - \\"lte\\": \\"3333-33-33T33:33:33.333Z\\", - \\"format\\": \\"strict_date_optional_time\\" - } - } - } - ] - } - } -}", - "{ - \\"version\\": true, - \\"size\\": 500, - \\"sort\\": [ - { - \\"time\\": { - \\"order\\": \\"desc\\", - \\"unmapped_type\\": \\"boolean\\" - } - } - ], - \\"aggs\\": { - \\"2\\": { - \\"date_histogram\\": { - \\"field\\": \\"time\\", - \\"fixed_interval\\": \\"30s\\", - \\"time_zone\\": \\"America/Los_Angeles\\", - \\"min_doc_count\\": 1 - } - } - }, - \\"_source\\": { - \\"excludes\\": [] - }, - \\"query\\": { - \\"bool\\": { - \\"filter\\": [ - { - \\"match_all\\": {} - }, - { - \\"range\\": { - \\"time\\": { - \\"gte\\": \\"2222-22-22T22:22:22.222Z\\", - \\"lte\\": \\"3333-33-33T33:33:33.333Z\\", - \\"format\\": \\"strict_date_optional_time\\" - } - } - } - ] - } - } -}", - ], -} -`; - -exports[`Utils class formatRequestBodyDoc changed with no-indenting 1`] = ` -Object { - "changed": true, - "data": Array [ - "{\\"data\\":{\\"long-max\\":18014398509481982,\\"long-min\\":-18014398509481982,\\"num\\":2}}", - "{\\"'\\\\\\"key\\\\\\"'\\":\\"'oddly' 'quoted \\\\\\"value\\\\\\"'\\",\\"obj\\":\\"{\\\\\\"nested\\\\\\": \\\\\\"inner-value\\\\\\", \\\\\\"inner-max\\\\\\": 18014398509481982\\"}", - "{\\"version\\":true,\\"size\\":500,\\"sort\\":[{\\"time\\":{\\"order\\":\\"desc\\",\\"unmapped_type\\":\\"boolean\\"}}],\\"aggs\\":{\\"2\\":{\\"date_histogram\\":{\\"field\\":\\"time\\",\\"fixed_interval\\":\\"30s\\",\\"time_zone\\":\\"America/Los_Angeles\\",\\"min_doc_count\\":1}}},\\"_source\\":{\\"excludes\\":[]},\\"query\\":{\\"bool\\":{\\"filter\\":[{\\"match_all\\":{}},{\\"range\\":{\\"time\\":{\\"gte\\":\\"2222-22-22T22:22:22.222Z\\",\\"lte\\":\\"3333-33-33T33:33:33.333Z\\",\\"format\\":\\"strict_date_optional_time\\"}}}]}}}", - "{\\"version\\":true,\\"size\\":500,\\"sort\\":[{\\"time\\":{\\"order\\":\\"desc\\",\\"unmapped_type\\":\\"boolean\\"}}],\\"aggs\\":{\\"2\\":{\\"date_histogram\\":{\\"field\\":\\"time\\",\\"fixed_interval\\":\\"30s\\",\\"time_zone\\":\\"America/Los_Angeles\\",\\"min_doc_count\\":1}}},\\"_source\\":{\\"excludes\\":[]},\\"query\\":{\\"bool\\":{\\"filter\\":[{\\"match_all\\":{}},{\\"range\\":{\\"time\\":{\\"gte\\":\\"2222-22-22T22:22:22.222Z\\",\\"lte\\":\\"3333-33-33T33:33:33.333Z\\",\\"format\\":\\"strict_date_optional_time\\"}}}]}}}", - ], -} -`; diff --git a/src/plugins/console/public/lib/utils/__tests__/utils.test.ts b/src/plugins/console/public/lib/utils/__tests__/utils.test.ts index 6004d963e27c..7f5f0c0de53f 100644 --- a/src/plugins/console/public/lib/utils/__tests__/utils.test.ts +++ b/src/plugins/console/public/lib/utils/__tests__/utils.test.ts @@ -29,7 +29,6 @@ */ import * as utils from '../'; -import { stringify } from '@osd/std'; describe('Utils class', () => { test('extract deprecation messages', function () { @@ -105,71 +104,4 @@ describe('Utils class', () => { 'e", f"', ]); }); - - describe('formatRequestBodyDoc', () => { - const longPositive = BigInt(Number.MAX_SAFE_INTEGER) * 2n; - const longNegative = BigInt(Number.MIN_SAFE_INTEGER) * 2n; - const sample = { - version: true, - size: 500, - sort: [{ time: { order: 'desc', unmapped_type: 'boolean' } }], - aggs: { - '2': { - date_histogram: { - field: 'time', - fixed_interval: '30s', - time_zone: 'America/Los_Angeles', - min_doc_count: 1, - }, - }, - }, - _source: { excludes: [] }, - query: { - bool: { - filter: [ - { - match_all: {}, - }, - { - range: { - time: { - gte: '2222-22-22T22:22:22.222Z', - lte: '3333-33-33T33:33:33.333Z', - format: 'strict_date_optional_time', - }, - }, - }, - ], - }, - }, - }; - const objStringSample = { - [`'"key"'`]: `'oddly' 'quoted "value"'`, - obj: `{"nested": "inner-value", "inner-max": ${longPositive}`, - }; - const data = [ - stringify({ data: { 'long-max': longPositive, 'long-min': longNegative, num: 2 } }), - JSON.stringify(objStringSample), - JSON.stringify(sample), - JSON.stringify(sample, null, 3), - ]; - - test('changed with indenting', () => { - expect(utils.formatRequestBodyDoc(data, true)).toMatchSnapshot(); - }); - - test('changed with no-indenting', () => { - expect(utils.formatRequestBodyDoc(data, false)).toMatchSnapshot(); - }); - - test('unchanged with indenting', () => { - const result = utils.formatRequestBodyDoc([JSON.stringify(sample, null, 2)], true); - expect(result.changed).toStrictEqual(false); - }); - - test('unchanged with no-indenting', () => { - const result = utils.formatRequestBodyDoc([JSON.stringify(sample)], false); - expect(result.changed).toStrictEqual(false); - }); - }); }); diff --git a/src/plugins/console/public/lib/utils/index.ts b/src/plugins/console/public/lib/utils/index.ts index 7ea4cd9b893f..93a0688ae725 100644 --- a/src/plugins/console/public/lib/utils/index.ts +++ b/src/plugins/console/public/lib/utils/index.ts @@ -28,7 +28,6 @@ * under the License. */ -import { parse, stringify } from '@osd/std'; import _ from 'lodash'; import { XJson } from '../../../../opensearch_ui_shared/public'; @@ -43,7 +42,7 @@ export function textFromRequest(request: any) { } export function jsonToString(data: any, indent: boolean) { - return stringify(data, null, indent ? 2 : 0); + return JSON.stringify(data, null, indent ? 2 : 0); } export function formatRequestBodyDoc(data: string[], indent: boolean) { @@ -52,7 +51,7 @@ export function formatRequestBodyDoc(data: string[], indent: boolean) { for (let i = 0; i < data.length; i++) { const curDoc = data[i]; try { - let newDoc = jsonToString(parse(collapseLiteralStrings(curDoc)), indent); + let newDoc = jsonToString(JSON.parse(collapseLiteralStrings(curDoc)), indent); if (indent) { newDoc = expandLiteralStrings(newDoc); } diff --git a/src/plugins/console/public/services/storage.ts b/src/plugins/console/public/services/storage.ts index 870904a4b3d8..f143b0cefcfe 100644 --- a/src/plugins/console/public/services/storage.ts +++ b/src/plugins/console/public/services/storage.ts @@ -29,7 +29,6 @@ */ import { transform, keys, startsWith } from 'lodash'; -import { parse, stringify } from '@osd/std'; type IStorageEngine = typeof window.localStorage; @@ -41,12 +40,12 @@ export class Storage { constructor(private readonly engine: IStorageEngine, private readonly prefix: string) {} encode(val: any) { - return stringify(val); + return JSON.stringify(val); } decode(val: any) { if (typeof val === 'string') { - return parse(val); + return JSON.parse(val); } } diff --git a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts index f2155011cd74..0a401ded813b 100644 --- a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts +++ b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts @@ -31,8 +31,8 @@ import { OpenSearchDashboardsRequest, RequestHandler } from 'opensearch-dashboards/server'; import { trimStart } from 'lodash'; import { Readable } from 'stream'; -import { stringify } from '@osd/std'; -import { ApiResponse } from '@opensearch-project/opensearch'; + +import { ApiResponse } from '@opensearch-project/opensearch/'; // eslint-disable-next-line @osd/eslint/no-restricted-paths import { ensureRawRequest } from '../../../../../../../core/server/http/router'; @@ -90,7 +90,7 @@ export const createHandler = ({ const { path, method, dataSourceId } = query; const client = dataSourceId ? await ctx.dataSource.opensearch.getClient(dataSourceId) - : ctx.core.opensearch.client.asCurrentUserWithLongNumeralsSupport; + : ctx.core.opensearch.client.asCurrentUser; let opensearchResponse: ApiResponse; if (!pathFilters.some((re) => re.test(path))) { @@ -116,24 +116,14 @@ export const createHandler = ({ { headers: requestHeaders } ); - const { - statusCode, - body: responseContent, - warnings, - headers: responseHeaders, - } = opensearchResponse; + const { statusCode, body: responseContent, warnings } = opensearchResponse; if (method.toUpperCase() !== 'HEAD') { - /* If a response is a parse JSON object, we need to use a custom `stringify` to handle BigInt - * values. - */ - const isJSONResponse = responseHeaders?.['content-type']?.includes?.('application/json'); return response.custom({ statusCode: statusCode!, - body: isJSONResponse ? stringify(responseContent) : responseContent, + body: responseContent, headers: { warning: warnings || '', - ...(isJSONResponse ? { 'Content-Type': 'application/json; charset=utf-8' } : {}), }, }); } @@ -149,7 +139,7 @@ export const createHandler = ({ } catch (e: any) { const isResponseErrorFlag = isResponseError(e); if (!isResponseError) log.error(e); - const errorMessage = isResponseErrorFlag ? stringify(e.meta.body) : e.message; + const errorMessage = isResponseErrorFlag ? JSON.stringify(e.meta.body) : e.message; // core http route handler has special logic that asks for stream readable input to pass error opaquely const errorResponseBody = new Readable({ read() { diff --git a/src/plugins/console/server/routes/api/console/proxy/tests/body.test.ts b/src/plugins/console/server/routes/api/console/proxy/tests/body.test.ts index 59008fc81684..95b243dd4734 100644 --- a/src/plugins/console/server/routes/api/console/proxy/tests/body.test.ts +++ b/src/plugins/console/server/routes/api/console/proxy/tests/body.test.ts @@ -50,9 +50,7 @@ describe('Console Proxy Route', () => { const requestHandlerContextMock = coreMock.createRequestHandlerContext(); opensearchClient = requestHandlerContextMock.opensearch.client; - opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mockResolvedValueOnce( - mockResponse - ); + opensearchClient.asCurrentUser.transport.request.mockResolvedValueOnce(mockResponse); const handler = createHandler(getProxyRouteHandlerDeps({})); return handler( diff --git a/src/plugins/console/server/routes/api/console/proxy/tests/headers.test.ts b/src/plugins/console/server/routes/api/console/proxy/tests/headers.test.ts index 7a6b3f70f490..a1964d160e2c 100644 --- a/src/plugins/console/server/routes/api/console/proxy/tests/headers.test.ts +++ b/src/plugins/console/server/routes/api/console/proxy/tests/headers.test.ts @@ -85,9 +85,7 @@ describe('Console Proxy Route', () => { opensearchDashboardsResponseFactory ); - const [ - [, opts], - ] = opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mock.calls; + const [[, opts]] = opensearchClient.asCurrentUser.transport.request.mock.calls; const headers = opts?.headers; expect(headers).to.have.property('x-forwarded-for'); expect(headers!['x-forwarded-for']).to.be('0.0.0.0'); diff --git a/src/plugins/console/server/routes/api/console/proxy/tests/params.test.ts b/src/plugins/console/server/routes/api/console/proxy/tests/params.test.ts index 2e191425752a..80523c8031df 100644 --- a/src/plugins/console/server/routes/api/console/proxy/tests/params.test.ts +++ b/src/plugins/console/server/routes/api/console/proxy/tests/params.test.ts @@ -75,9 +75,7 @@ describe('Console Proxy Route', () => { ); const mockResponse = opensearchServiceMock.createSuccessTransportRequestPromise('foo'); - opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mockResolvedValueOnce( - mockResponse - ); + opensearchClient.asCurrentUser.transport.request.mockResolvedValueOnce(mockResponse); const { status } = await handler( { core: requestHandlerContextMock, dataSource: {} as any }, @@ -95,9 +93,7 @@ describe('Console Proxy Route', () => { ); const mockResponse = opensearchServiceMock.createSuccessTransportRequestPromise('foo'); - opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mockResolvedValueOnce( - mockResponse - ); + opensearchClient.asCurrentUser.transport.request.mockResolvedValueOnce(mockResponse); const { status } = await handler( { core: requestHandlerContextMock, dataSource: {} as any }, diff --git a/src/plugins/console/server/routes/api/console/proxy/tests/query_string.test.ts b/src/plugins/console/server/routes/api/console/proxy/tests/query_string.test.ts index edb3ec6e6418..9aefea1182cc 100644 --- a/src/plugins/console/server/routes/api/console/proxy/tests/query_string.test.ts +++ b/src/plugins/console/server/routes/api/console/proxy/tests/query_string.test.ts @@ -63,9 +63,7 @@ describe('Console Proxy Route', () => { describe('contains full url', () => { it('treats the url as a path', async () => { await request('GET', 'http://evil.com/test'); - const [ - [args], - ] = opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mock.calls; + const [[args]] = opensearchClient.asCurrentUser.transport.request.mock.calls; expect(args.path).toBe('/http://evil.com/test?pretty=true'); }); @@ -73,18 +71,14 @@ describe('Console Proxy Route', () => { describe('starts with a slash', () => { it('keeps as it is', async () => { await request('GET', '/index/id'); - const [ - [args], - ] = opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mock.calls; + const [[args]] = opensearchClient.asCurrentUser.transport.request.mock.calls; expect(args.path).toBe('/index/id?pretty=true'); }); }); describe(`doesn't start with a slash`, () => { it('adds slash to path before sending request', async () => { await request('GET', 'index/id'); - const [ - [args], - ] = opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mock.calls; + const [[args]] = opensearchClient.asCurrentUser.transport.request.mock.calls; expect(args.path).toBe('/index/id?pretty=true'); }); }); @@ -92,9 +86,7 @@ describe('Console Proxy Route', () => { describe(`contains query parameter`, () => { it('adds slash to path before sending request', async () => { await request('GET', '_cat/tasks?v'); - const [ - [args], - ] = opensearchClient.asCurrentUserWithLongNumeralsSupport.transport.request.mock.calls; + const [[args]] = opensearchClient.asCurrentUser.transport.request.mock.calls; expect(args.path).toBe('/_cat/tasks?v=&pretty=true'); }); }); diff --git a/src/plugins/opensearch_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts b/src/plugins/opensearch_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts index 9860be7800e4..9f42e669b280 100644 --- a/src/plugins/opensearch_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts +++ b/src/plugins/opensearch_ui_shared/__packages_do_not_import__/xjson/json_xjson_translation_tools/index.ts @@ -28,13 +28,12 @@ * under the License. */ -import { parse, stringify } from '@osd/std'; import { extractJSONStringValues } from './parser'; export function collapseLiteralStrings(data: string) { const splitData = data.split(`"""`); for (let idx = 1; idx < splitData.length - 1; idx += 2) { - splitData[idx] = stringify(splitData[idx]); + splitData[idx] = JSON.stringify(splitData[idx]); } return splitData.join(''); } @@ -80,7 +79,7 @@ export function expandLiteralStrings(data: string) { (candidate[candidate.length - 2] === '"' && candidate[candidate.length - 3] === '\\'); if (!skip && candidate.match(/\\./)) { - result += `"""${parse(candidate)}"""`; + result += `"""${JSON.parse(candidate)}"""`; } else { result += candidate; } diff --git a/yarn.lock b/yarn.lock index 88f7dd435331..d71aa1d447e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2497,10 +2497,10 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@opensearch-project/opensearch-next@npm:@opensearch-project/opensearch@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.3.1.tgz#3596e2f1f0615a7555102f6f941f0e0ec645c2cd" - integrity sha512-Kg8tddAx6sinStnNi6IeGilfvLWlonIxaRdVNiJcNPr1yMqd0c9TSegn18zKr0Pb0IM9xBIGBSkRPuh67ZN6Hw== +"@opensearch-project/opensearch-next@npm:@opensearch-project/opensearch@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.2.1.tgz#a400203afa6512ef73945663163a404763a10f5a" + integrity sha512-8zfQX1acL9eWG+ohIc9nJVT9LSqXCdbVEJs0rCPRtji3XF6ahzsiKmGNTeWLxCPDxWCjAIWq9t95xP3Y5Egi6Q== dependencies: aws4 "^1.11.0" debug "^4.3.1"