Skip to content

Commit

Permalink
Merge branch 'main' into upload-artifact-v4
Browse files Browse the repository at this point in the history
  • Loading branch information
Hailong-am authored Nov 18, 2024
2 parents 54d110a + ab4e6e8 commit 7438bbb
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 55 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8839.yml
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 2 additions & 0 deletions changelogs/fragments/8866.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- Hide Date Picker for Unsupported Types ([#8866](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8866))
2 changes: 2 additions & 0 deletions changelogs/fragments/8871.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- Search on page load out of sync state when clicking submit. ([#8871](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8871))
84 changes: 81 additions & 3 deletions packages/osd-std/src/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import JSON11 from 'json11';
import { stringify, parse } from './json';

describe('json', () => {
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion packages/osd-std/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
9 changes: 9 additions & 0 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/lib/Serializer.js', [
{
from: 'val < Number.MAX_SAFE_INTEGER',
to: 'val < Number.MIN_SAFE_INTEGER',
},
])
);

await Promise.all(promises);
};
Expand Down
100 changes: 70 additions & 30 deletions src/dev/generate_release_note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { ToolingLog } from '@osd/dev-utils';
import { join, resolve } from 'path';
import { readFileSync, writeFileSync, Dirent, rm, rename, promises as fsPromises } from 'fs';
import { load as loadYaml } from 'js-yaml';
Expand All @@ -19,6 +20,11 @@ import {
filePath,
} from './generate_release_note_helper';

const log = new ToolingLog({
level: 'info',
writeTo: process.stdout,
});

// Function to add content after the 'Unreleased' section in the changelog
function addContentAfterUnreleased(path: string, newContent: string): void {
let fileContent = readFileSync(path, 'utf8');
Expand Down Expand Up @@ -60,35 +66,63 @@ async function readFragments() {
) as unknown) as Changelog;

const fragmentPaths = await readdir(fragmentDirPath, { withFileTypes: true });
const failedFragments: string[] = [];

for (const fragmentFilename of fragmentPaths) {
// skip non yml or yaml files
if (!/\.ya?ml$/i.test(fragmentFilename.name)) {
// eslint-disable-next-line no-console
console.warn(`Skipping non yml or yaml file ${fragmentFilename.name}`);
log.info(`Skipping non yml or yaml file ${fragmentFilename.name}`);
continue;
}

const fragmentPath = join(fragmentDirPath, fragmentFilename.name);
const fragmentContents = readFileSync(fragmentPath, { encoding: 'utf-8' });

validateFragment(fragmentContents);

const fragmentContentLines = fragmentContents.split('\n');
// Adding a quotes to the second line and escaping exisiting " within the line
fragmentContentLines[1] = fragmentContentLines[1].replace(/-\s*(.*)/, (match, p1) => {
// Escape any existing quotes in the content
const escapedContent = p1.replace(/"/g, '\\"');
return `- "${escapedContent}"`;
});

const processedFragmentContent = fragmentContentLines.join('\n');

const fragmentYaml = loadYaml(processedFragmentContent) as Changelog;
for (const [sectionKey, entries] of Object.entries(fragmentYaml)) {
sections[sectionKey as SectionKey].push(...entries);
try {
const fragmentPath = join(fragmentDirPath, fragmentFilename.name);
const fragmentContents = readFileSync(fragmentPath, { encoding: 'utf-8' });

try {
validateFragment(fragmentContents);
} catch (validationError) {
log.info(`Validation failed for ${fragmentFilename.name}: ${validationError.message}`);
failedFragments.push(
`${fragmentFilename.name} (Validation Error: ${validationError.message})`
);
continue;
}

const fragmentContentLines = fragmentContents.split('\n');
// Adding a quotes to the second line and escaping existing " within the line
fragmentContentLines[1] = fragmentContentLines[1].replace(/-\s*(.*)/, (match, p1) => {
// Escape any existing quotes in the content
const escapedContent = p1.replace(/"/g, '\\"');
return `- "${escapedContent}"`;
});

const processedFragmentContent = fragmentContentLines.join('\n');

try {
const fragmentYaml = loadYaml(processedFragmentContent) as Changelog;
for (const [sectionKey, entries] of Object.entries(fragmentYaml)) {
sections[sectionKey as SectionKey].push(...entries);
}
} catch (yamlError) {
log.info(`Failed to parse YAML in ${fragmentFilename.name}: ${yamlError.message}`);
failedFragments.push(`${fragmentFilename.name} (YAML Parse Error: ${yamlError.message})`);
continue;
}
} catch (error) {
log.info(`Failed to process ${fragmentFilename.name}: ${error.message}`);
failedFragments.push(`${fragmentFilename.name} (Processing Error: ${error.message})`);
continue;
}
}
return { sections, fragmentPaths };

if (failedFragments.length > 0) {
log.info('\nThe following changelog fragments were skipped due to errors:');
failedFragments.forEach((fragment) => log.info(`- ${fragment}`));
log.info('\nPlease review and fix these fragments for inclusion in the next release.\n');
}

return { sections, fragmentPaths, failedFragments };
}

async function moveFragments(fragmentPaths: Dirent[], fragmentTempDirPath: string): Promise<void> {
Expand Down Expand Up @@ -128,16 +162,22 @@ function generateReleaseNote(changelogSections: string[]) {
}

(async () => {
const { sections, fragmentPaths } = await readFragments();
// create folder for temp fragments
const fragmentTempDirPath = await fsPromises.mkdtemp(join(fragmentDirPath, 'tmp_fragments-'));
// move fragments to temp fragments folder
await moveFragments(fragmentPaths, fragmentTempDirPath);
const { sections, fragmentPaths, failedFragments } = await readFragments();

const changelogSections = generateChangelog(sections);
// Only proceed if we have some valid fragments
if (Object.values(sections).some((section) => section.length > 0)) {
// create folder for temp fragments
const fragmentTempDirPath = await fsPromises.mkdtemp(join(fragmentDirPath, 'tmp_fragments-'));
// move fragments to temp fragments folder
await moveFragments(fragmentPaths, fragmentTempDirPath);

generateReleaseNote(changelogSections);
const changelogSections = generateChangelog(sections);
generateReleaseNote(changelogSections);

// remove temp fragments folder
await deleteFragments(fragmentTempDirPath);
// remove temp fragments folder
await deleteFragments(fragmentTempDirPath);
} else {
log.error('No valid changelog entries were found. Release notes generation aborted.');
process.exit(1);
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { createEditor, DQLBody, SingleLineInput } from '../../../ui';
import { LanguageServiceContract } from './language_service';
import { LanguageConfig } from './types';

Expand All @@ -14,7 +15,7 @@ const createSetupLanguageServiceMock = (): jest.Mocked<LanguageServiceContract>
title: 'DQL',
search: {} as any,
getQueryString: jest.fn(),
editor: {} as any,
editor: createEditor(SingleLineInput, SingleLineInput, [], DQLBody),
fields: {
filterable: true,
visualizable: true,
Expand All @@ -28,7 +29,7 @@ const createSetupLanguageServiceMock = (): jest.Mocked<LanguageServiceContract>
title: 'Lucene',
search: {} as any,
getQueryString: jest.fn(),
editor: {} as any,
editor: createEditor(SingleLineInput, SingleLineInput, [], DQLBody),
fields: {
filterable: true,
visualizable: true,
Expand All @@ -42,7 +43,9 @@ const createSetupLanguageServiceMock = (): jest.Mocked<LanguageServiceContract>

return {
__enhance: jest.fn(),
registerLanguage: jest.fn(),
registerLanguage: jest.fn((language: LanguageConfig) => {
languages.set(language.id, language);
}),
getLanguage: jest.fn((id: string) => languages.get(id)),
getLanguages: jest.fn(() => Array.from(languages.values())),
getDefaultLanguage: jest.fn(() => languages.get('kuery') || languages.values().next().value),
Expand Down
Loading

0 comments on commit 7438bbb

Please sign in to comment.