mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Console] Fix auto-indentation issues (#214358)
Fixes https://github.com/elastic/kibana/issues/210231 Fixes https://github.com/elastic/kibana/issues/212499 ## Summary Test request: ``` GET _ingest/pipeline/_simulate { "docs": [ { "_source": { "trace": { "name": "GET /actuator/health/**" }, "transaction": { "outcome": "success" } } }, { "_source": { "vulnerability": { "reference": [ "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15778" ] } } } ] } ```
This commit is contained in:
parent
0aa226cad4
commit
18aa055a6a
4 changed files with 138 additions and 14 deletions
|
@ -76,8 +76,8 @@ export const MonacoEditor = ({ localStorageValue, value, setValue }: EditorProps
|
|||
}, [docLinkVersion]);
|
||||
|
||||
const autoIndentCallback = useCallback(async () => {
|
||||
return actionsProvider.current!.autoIndent();
|
||||
}, []);
|
||||
return actionsProvider.current!.autoIndent(context);
|
||||
}, [context]);
|
||||
|
||||
const sendRequestsCallback = useCallback(async () => {
|
||||
await actionsProvider.current?.sendRequests(dispatch, context);
|
||||
|
@ -103,7 +103,7 @@ export const MonacoEditor = ({ localStorageValue, value, setValue }: EditorProps
|
|||
registerKeyboardCommands({
|
||||
editor: editorInstance,
|
||||
sendRequest: sendRequestsCallback,
|
||||
autoIndent: async () => await actionsProvider.current?.autoIndent(),
|
||||
autoIndent: async () => await actionsProvider.current?.autoIndent(context),
|
||||
getDocumentationLink: getDocumenationLink,
|
||||
moveToPreviousRequestEdge: async () =>
|
||||
await actionsProvider.current?.moveToPreviousRequestEdge(),
|
||||
|
@ -119,6 +119,7 @@ export const MonacoEditor = ({ localStorageValue, value, setValue }: EditorProps
|
|||
registerKeyboardCommands,
|
||||
unregisterKeyboardCommands,
|
||||
settings.isKeyboardShortcutsEnabled,
|
||||
context,
|
||||
]);
|
||||
|
||||
const editorWillUnmountCallback = useCallback(() => {
|
||||
|
|
|
@ -620,7 +620,11 @@ export class MonacoEditorActionsProvider {
|
|||
/**
|
||||
* This function applies indentations to the request in the selected text.
|
||||
*/
|
||||
public async autoIndent() {
|
||||
public async autoIndent(context: ContextValue) {
|
||||
const {
|
||||
services: { notifications },
|
||||
} = context;
|
||||
const { toasts } = notifications;
|
||||
const parsedRequests = await this.getSelectedParsedRequests();
|
||||
const selectionStartLineNumber = parsedRequests[0].startLineNumber;
|
||||
const selectionEndLineNumber = parsedRequests[parsedRequests.length - 1].endLineNumber;
|
||||
|
@ -638,7 +642,12 @@ export class MonacoEditorActionsProvider {
|
|||
const selectedText = this.getTextInRange(selectedRange);
|
||||
const allText = this.getTextInRange();
|
||||
|
||||
const autoIndentedText = getAutoIndentedRequests(parsedRequests, selectedText, allText);
|
||||
const autoIndentedText = getAutoIndentedRequests(
|
||||
parsedRequests,
|
||||
selectedText,
|
||||
allText,
|
||||
(text) => toasts.addWarning(text)
|
||||
);
|
||||
|
||||
this.editor.executeEdits(AUTO_INDENTATION_ACTION_LABEL, [
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
replaceRequestVariables,
|
||||
trackSentRequests,
|
||||
getRequestFromEditor,
|
||||
containsComments,
|
||||
} from './requests_utils';
|
||||
|
||||
describe('requests_utils', () => {
|
||||
|
@ -168,6 +169,7 @@ describe('requests_utils', () => {
|
|||
});
|
||||
|
||||
describe('getAutoIndentedRequests', () => {
|
||||
const mockAddToastWarning = jest.fn();
|
||||
const sampleEditorTextLines = [
|
||||
' ', // line 1
|
||||
'GET _search ', // line 2
|
||||
|
@ -241,7 +243,8 @@ describe('requests_utils', () => {
|
|||
sampleEditorTextLines
|
||||
.slice(TEST_REQUEST_1.startLineNumber - 1, TEST_REQUEST_1.endLineNumber)
|
||||
.join('\n'),
|
||||
sampleEditorTextLines.join('\n')
|
||||
sampleEditorTextLines.join('\n'),
|
||||
mockAddToastWarning
|
||||
);
|
||||
const expectedResultLines = [
|
||||
'GET _search',
|
||||
|
@ -253,6 +256,7 @@ describe('requests_utils', () => {
|
|||
];
|
||||
|
||||
expect(formattedData).toBe(expectedResultLines.join('\n'));
|
||||
expect(mockAddToastWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('correctly auto-indents a single request with no data', () => {
|
||||
|
@ -261,11 +265,13 @@ describe('requests_utils', () => {
|
|||
sampleEditorTextLines
|
||||
.slice(TEST_REQUEST_2.startLineNumber - 1, TEST_REQUEST_2.endLineNumber)
|
||||
.join('\n'),
|
||||
sampleEditorTextLines.join('\n')
|
||||
sampleEditorTextLines.join('\n'),
|
||||
mockAddToastWarning
|
||||
);
|
||||
const expectedResult = 'GET _all';
|
||||
|
||||
expect(formattedData).toBe(expectedResult);
|
||||
expect(mockAddToastWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('correctly auto-indents a single request with multiple data', () => {
|
||||
|
@ -274,7 +280,8 @@ describe('requests_utils', () => {
|
|||
sampleEditorTextLines
|
||||
.slice(TEST_REQUEST_3.startLineNumber - 1, TEST_REQUEST_3.endLineNumber)
|
||||
.join('\n'),
|
||||
sampleEditorTextLines.join('\n')
|
||||
sampleEditorTextLines.join('\n'),
|
||||
mockAddToastWarning
|
||||
);
|
||||
const expectedResultLines = [
|
||||
'POST /_bulk',
|
||||
|
@ -292,13 +299,15 @@ describe('requests_utils', () => {
|
|||
];
|
||||
|
||||
expect(formattedData).toBe(expectedResultLines.join('\n'));
|
||||
expect(mockAddToastWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('auto-indents multiple request with comments in between', () => {
|
||||
const formattedData = getAutoIndentedRequests(
|
||||
[TEST_REQUEST_1, TEST_REQUEST_2, TEST_REQUEST_3],
|
||||
sampleEditorTextLines.slice(1, 23).join('\n'),
|
||||
sampleEditorTextLines.join('\n')
|
||||
sampleEditorTextLines.join('\n'),
|
||||
mockAddToastWarning
|
||||
);
|
||||
const expectedResultLines = [
|
||||
'GET _search',
|
||||
|
@ -329,6 +338,7 @@ describe('requests_utils', () => {
|
|||
];
|
||||
|
||||
expect(formattedData).toBe(expectedResultLines.join('\n'));
|
||||
expect(mockAddToastWarning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`auto-indents method line but doesn't auto-indent data with comments`, () => {
|
||||
|
@ -339,10 +349,16 @@ describe('requests_utils', () => {
|
|||
const formattedData = getAutoIndentedRequests(
|
||||
[TEST_REQUEST_4],
|
||||
`${methodLine}\n${dataText}`,
|
||||
sampleEditorTextLines.join('\n')
|
||||
sampleEditorTextLines.join('\n'),
|
||||
mockAddToastWarning
|
||||
);
|
||||
|
||||
expect(formattedData).toBe(`GET _search // test comment\n${dataText}`);
|
||||
expect(mockAddToastWarning).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'Auto-indentation is currently not supported for requests containing comments. Please remove comments to enable formatting.'
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -469,4 +485,75 @@ describe('requests_utils', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('containsComments', () => {
|
||||
it('should return false for JSON with // and /* inside strings', () => {
|
||||
const requestData = `{
|
||||
"docs": [
|
||||
{
|
||||
"_source": {
|
||||
"trace": {
|
||||
"name": "GET /actuator/health/**"
|
||||
},
|
||||
"transaction": {
|
||||
"outcome": "success"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_source": {
|
||||
"vulnerability": {
|
||||
"reference": [
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15778"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for text with actual line comment', () => {
|
||||
const requestData = `{
|
||||
// This is a comment
|
||||
"query": { "match_all": {} }
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for text with actual block comment', () => {
|
||||
const requestData = `{
|
||||
/* Bulk insert */
|
||||
"index": { "_index": "test" },
|
||||
"field1": "value1"
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for text without any comments', () => {
|
||||
const requestData = `{
|
||||
"field": "value"
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for empty string', () => {
|
||||
expect(containsComments('')).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly handle escaped quotes within strings', () => {
|
||||
const requestData = `{
|
||||
"field": \"value with \\\"escaped quotes\\\"\"
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if comment is outside of strings', () => {
|
||||
const requestData = `{
|
||||
"field": "value" // comment here
|
||||
}`;
|
||||
expect(containsComments(requestData)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
import { monaco, ParsedRequest } from '@kbn/monaco';
|
||||
import { parse } from 'hjson';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { constructUrl } from '../../../../lib/es';
|
||||
import type { MetricsTracker } from '../../../../types';
|
||||
import type { DevToolsVariable } from '../../../components';
|
||||
|
@ -141,7 +142,8 @@ export const getRequestEndLineNumber = ({
|
|||
export const getAutoIndentedRequests = (
|
||||
requests: AdjustedParsedRequest[],
|
||||
selectedText: string,
|
||||
allText: string
|
||||
allText: string,
|
||||
addToastWarning: (text: string) => void
|
||||
): string => {
|
||||
const selectedTextLines = selectedText.split(`\n`);
|
||||
const allTextLines = allText.split(`\n`);
|
||||
|
@ -162,10 +164,16 @@ export const getAutoIndentedRequests = (
|
|||
const firstLine = cleanUpWhitespaces(requestLines[0]);
|
||||
formattedTextLines.push(firstLine);
|
||||
const dataLines = requestLines.slice(1);
|
||||
if (dataLines.some((line) => containsComments(line))) {
|
||||
if (containsComments(dataLines.join(''))) {
|
||||
// If data has comments, add it as it is - without formatting
|
||||
// TODO: Format requests with comments https://github.com/elastic/kibana/issues/182138
|
||||
formattedTextLines.push(...dataLines);
|
||||
addToastWarning(
|
||||
i18n.translate('console.notification.monaco.warning.nonSupportedAutoindentation', {
|
||||
defaultMessage:
|
||||
'Auto-indentation is currently not supported for requests containing comments. Please remove comments to enable formatting.',
|
||||
})
|
||||
);
|
||||
} else {
|
||||
// If no comments, indent data
|
||||
if (requestLines.length > 1) {
|
||||
|
@ -224,8 +232,27 @@ export const getRequestFromEditor = (
|
|||
return { method: upperCaseMethod, url, data };
|
||||
};
|
||||
|
||||
export const containsComments = (text: string) => {
|
||||
return text.indexOf('//') >= 0 || text.indexOf('/*') >= 0;
|
||||
export const containsComments = (requestData: string) => {
|
||||
let insideString = false;
|
||||
let prevChar = '';
|
||||
for (let i = 0; i < requestData.length; i++) {
|
||||
const char = requestData[i];
|
||||
const nextChar = requestData[i + 1];
|
||||
|
||||
if (!insideString && char === '"') {
|
||||
insideString = true;
|
||||
} else if (insideString && char === '"' && prevChar !== '\\') {
|
||||
insideString = false;
|
||||
} else if (!insideString) {
|
||||
if (char === '/' && (nextChar === '/' || nextChar === '*')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
prevChar = char;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const indentData = (dataString: string): string => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue