mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Console] Improve status code highlighting (#192888)
Closes https://github.com/elastic/kibana/issues/190731 ## Summary This PR improves the status code highlighting when multiple requests are sent. https://github.com/user-attachments/assets/a0e8ba07-534d-4a48-aa7a-3964adc0e540 Dark mode: <img width="1495" alt="Screenshot 2024-09-17 at 12 02 36" src="https://github.com/user-attachments/assets/140e211d-57c2-48b7-abb8-09cfb4e24366"> All code statuses: <img width="860" alt="Screenshot 2024-09-17 at 11 42 22" src="https://github.com/user-attachments/assets/9509f9cd-ec60-44e2-b700-afaea595ad13">
This commit is contained in:
parent
e546302048
commit
61d0b7f0d6
10 changed files with 141 additions and 165 deletions
|
@ -25,21 +25,24 @@ export const consoleOutputLexerRules: monaco.languages.IMonarchLanguage = {
|
|||
comments: [
|
||||
// Line comment indicated by #
|
||||
// Everything after the # character is matched, stopping right before the status code and status text at the end if they are present
|
||||
matchTokensWithEOL('comment', /# .+?(?=\s+\d{3}(?: \w+)*$)/, 'root', 'status'),
|
||||
matchTokensWithEOL('comment.default', /# .+?(?=\s+\[\b1\d{2}(?: \w+)*\]$)/, 'root', 'status'),
|
||||
matchTokensWithEOL('comment.success', /# .+?(?=\s+\[\b2\d{2}(?: \w+)*\]$)/, 'root', 'status'),
|
||||
matchTokensWithEOL('comment.primary', /# .+?(?=\s+\[\b3\d{2}(?: \w+)*\]$)/, 'root', 'status'),
|
||||
matchTokensWithEOL('comment.warning', /# .+?(?=\s+\[\b4\d{2}(?: \w+)*\]$)/, 'root', 'status'),
|
||||
matchTokensWithEOL('comment.danger', /# .+?(?=\s+\[\b5\d{2}(?: \w+)*\]$)/, 'root', 'status'),
|
||||
...consoleSharedLexerRules.tokenizer.comments,
|
||||
],
|
||||
status: [
|
||||
// Following HTTP response status codes conventions
|
||||
// Informational responses (status codes 100 – 199)
|
||||
matchTokensWithEOL('status.info', /\b1\d{2}(?: \w+)*$/, 'root'),
|
||||
// Successful responses (status codes 200 – 299)
|
||||
matchTokensWithEOL('status.success', /\b2\d{2}(?: \w+)*$/, 'root'),
|
||||
// Redirection messages (status codes 300 – 399)
|
||||
matchTokensWithEOL('status.redirect', /\b3\d{2}(?: \w+)*$/, 'root'),
|
||||
// Client error responses (status codes 400 – 499)
|
||||
matchTokensWithEOL('status.warning', /\b4\d{2}(?: \w+)*$/, 'root'),
|
||||
// Server error responses (status codes 500 – 599)
|
||||
matchTokensWithEOL('status.error', /\b5\d{2}(?: \w+)*$/, 'root'),
|
||||
// Status codes 100 – 199
|
||||
matchTokensWithEOL('status.default', /\[\b1\d{2}(?: \w+)*\]$/, 'root'),
|
||||
// Status codes 200 – 299
|
||||
matchTokensWithEOL('status.success', /\[\b2\d{2}(?: \w+)*\]$/, 'root'),
|
||||
// Status codes 300 – 399
|
||||
matchTokensWithEOL('status.primary', /\[\b3\d{2}(?: \w+)*\]$/, 'root'),
|
||||
// Status codes 400 – 499
|
||||
matchTokensWithEOL('status.warning', /\[\b4\d{2}(?: \w+)*\]$/, 'root'),
|
||||
// Status codes 500 – 599
|
||||
matchTokensWithEOL('status.danger', /\[\b5\d{2}(?: \w+)*\]$/, 'root'),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -20,6 +20,11 @@ const background = euiThemeVars.euiFormBackgroundColor;
|
|||
const booleanTextColor = '#585CF6';
|
||||
const methodTextColor = '#DD0A73';
|
||||
const urlTextColor = '#00A69B';
|
||||
const defaultStatusBackgroundColor = darkMode ? '#191B20' : '#F7F8FA';
|
||||
const successStatusBackgroundColor = darkMode ? '#212B30' : '#E7F5F5';
|
||||
const primaryStatusBackgroundColor = darkMode ? '#1E232D' : '#EBF1F7';
|
||||
const warningStatusBackgroundColor = darkMode ? '#2C2B25' : '#FBF6E9';
|
||||
const dangerStatusBackgroundColor = darkMode ? '#2E2024' : '#F6E6E7';
|
||||
export const buildConsoleTheme = (): monaco.editor.IStandaloneThemeData => {
|
||||
const euiTheme = darkMode ? buildDarkTheme() : buildLightTheme();
|
||||
return {
|
||||
|
@ -39,27 +44,56 @@ export const buildConsoleTheme = (): monaco.editor.IStandaloneThemeData => {
|
|||
makeHighContrastColor(euiThemeVars.euiColorAccentText)(background)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.info'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(background)
|
||||
['comment.default'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(defaultStatusBackgroundColor)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['comment.success'],
|
||||
makeHighContrastColor(euiThemeVars.euiColorSuccessText)(successStatusBackgroundColor)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['comment.primary'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(primaryStatusBackgroundColor)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['comment.warning'],
|
||||
makeHighContrastColor(euiThemeVars.euiColorWarningText)(warningStatusBackgroundColor)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['comment.danger'],
|
||||
makeHighContrastColor(euiThemeVars.euiColorDangerText)(dangerStatusBackgroundColor)
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.default'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(defaultStatusBackgroundColor),
|
||||
true
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.success'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(euiThemeVars.euiColorSuccess)
|
||||
makeHighContrastColor(euiThemeVars.euiColorSuccessText)(successStatusBackgroundColor),
|
||||
true
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.redirect'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(background)
|
||||
['status.primary'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(primaryStatusBackgroundColor),
|
||||
true
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.warning'],
|
||||
makeHighContrastColor(euiThemeVars.euiTextColor)(euiThemeVars.euiColorWarning)
|
||||
makeHighContrastColor(euiThemeVars.euiColorWarningText)(warningStatusBackgroundColor),
|
||||
true
|
||||
),
|
||||
...buildRuleGroup(
|
||||
['status.error'],
|
||||
makeHighContrastColor('#FFFFFF')(euiThemeVars.euiColorDanger)
|
||||
['status.danger'],
|
||||
makeHighContrastColor(euiThemeVars.euiColorDangerText)(dangerStatusBackgroundColor),
|
||||
true
|
||||
),
|
||||
...buildRuleGroup(['method'], makeHighContrastColor(methodTextColor)(background)),
|
||||
...buildRuleGroup(['url'], makeHighContrastColor(urlTextColor)(background)),
|
||||
],
|
||||
colors: {
|
||||
...euiTheme.colors,
|
||||
'editorLineNumber.foreground': euiThemeVars.euiTextColor,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -17,11 +17,7 @@ export const SELECTED_REQUESTS_CLASSNAME = 'console__monaco_editor__selectedRequ
|
|||
/*
|
||||
* CSS class names used for the styling of multiple-response status codes
|
||||
*/
|
||||
export const PRIMARY_STATUS_BADGE_CLASSNAME = 'monaco__status_badge--primary';
|
||||
export const SUCCESS_STATUS_BADGE_CLASSNAME = 'monaco__status_badge--success';
|
||||
export const DEFAULT_STATUS_BADGE_CLASSNAME = 'monaco__status_badge--default';
|
||||
export const WARNING_STATUS_BADGE_CLASSNAME = 'monaco__status_badge--warning';
|
||||
export const DANGER_STATUS_BADGE_CLASSNAME = 'monaco__status_badge--danger';
|
||||
export const STATUS_CODE_LINE_CLASSNAME = 'monaco__status_code_line';
|
||||
|
||||
export const whitespacesRegex = /\s+/;
|
||||
export const newLineRegex = /\n/;
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
export {
|
||||
AutocompleteType,
|
||||
SELECTED_REQUESTS_CLASSNAME,
|
||||
SUCCESS_STATUS_BADGE_CLASSNAME,
|
||||
WARNING_STATUS_BADGE_CLASSNAME,
|
||||
PRIMARY_STATUS_BADGE_CLASSNAME,
|
||||
DEFAULT_STATUS_BADGE_CLASSNAME,
|
||||
DANGER_STATUS_BADGE_CLASSNAME,
|
||||
STATUS_CODE_LINE_CLASSNAME,
|
||||
} from './constants';
|
||||
export {
|
||||
getRequestStartLineNumber,
|
||||
|
|
|
@ -8,13 +8,17 @@
|
|||
*/
|
||||
|
||||
import { getStatusCodeDecorations } from './status_code_decoration_utils';
|
||||
import {
|
||||
SUCCESS_STATUS_BADGE_CLASSNAME,
|
||||
WARNING_STATUS_BADGE_CLASSNAME,
|
||||
DANGER_STATUS_BADGE_CLASSNAME,
|
||||
} from './constants';
|
||||
import { STATUS_CODE_LINE_CLASSNAME } from './constants';
|
||||
import { RequestResult } from '../../../hooks/use_send_current_request/send_request';
|
||||
|
||||
const SUCCESS_STATUS_CODE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}--success`;
|
||||
const WARNING_STATUS_CODE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}--warning`;
|
||||
const DANGER_STATUS_CODE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}--danger`;
|
||||
|
||||
const SUCCESS_STATUS_CODE_LINE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}_number--success`;
|
||||
const WARNING_STATUS_CODE_LINE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}_number--warning`;
|
||||
const DANGER_STATUS_CODE_LINE_CLASSNAME = `${STATUS_CODE_LINE_CLASSNAME}_number--danger`;
|
||||
|
||||
describe('getStatusCodeDecorations', () => {
|
||||
it('correctly returns all decorations on full data', () => {
|
||||
// Sample multiple-response data returned from ES:
|
||||
|
@ -91,108 +95,45 @@ describe('getStatusCodeDecorations', () => {
|
|||
const EXPECTED_DECORATIONS = [
|
||||
{
|
||||
range: {
|
||||
endColumn: 21,
|
||||
endColumn: 1,
|
||||
endLineNumber: 1,
|
||||
startColumn: 15,
|
||||
startColumn: 1,
|
||||
startLineNumber: 1,
|
||||
},
|
||||
options: {
|
||||
inlineClassName: SUCCESS_STATUS_BADGE_CLASSNAME,
|
||||
isWholeLine: true,
|
||||
blockClassName: SUCCESS_STATUS_CODE_CLASSNAME,
|
||||
marginClassName: SUCCESS_STATUS_CODE_LINE_CLASSNAME,
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
endColumn: 28,
|
||||
endColumn: 1,
|
||||
endLineNumber: 12,
|
||||
startColumn: 13,
|
||||
startColumn: 1,
|
||||
startLineNumber: 12,
|
||||
},
|
||||
options: {
|
||||
inlineClassName: WARNING_STATUS_BADGE_CLASSNAME,
|
||||
isWholeLine: true,
|
||||
blockClassName: WARNING_STATUS_CODE_CLASSNAME,
|
||||
marginClassName: WARNING_STATUS_CODE_LINE_CLASSNAME,
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
endColumn: 47,
|
||||
endColumn: 1,
|
||||
endLineNumber: 18,
|
||||
startColumn: 22,
|
||||
startColumn: 1,
|
||||
startLineNumber: 18,
|
||||
},
|
||||
options: {
|
||||
inlineClassName: DANGER_STATUS_BADGE_CLASSNAME,
|
||||
isWholeLine: true,
|
||||
blockClassName: DANGER_STATUS_CODE_CLASSNAME,
|
||||
marginClassName: DANGER_STATUS_CODE_LINE_CLASSNAME,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(getStatusCodeDecorations(SAMPLE_COMPLETE_DATA)).toEqual(EXPECTED_DECORATIONS);
|
||||
});
|
||||
|
||||
it('only returns decorations for data with complete status code and text', () => {
|
||||
// This sample data is same as in previous test but some of it has incomplete status code or status text
|
||||
const SAMPLE_INCOMPLETE_DATA: RequestResult[] = [
|
||||
{
|
||||
response: {
|
||||
timeMs: 50,
|
||||
// @ts-ignore
|
||||
statusCode: undefined,
|
||||
statusText: 'OK',
|
||||
contentType: 'application/json',
|
||||
value:
|
||||
'# GET _search OK\n{\n"took": 1,\n"timed_out": false,\n"hits": {\n"total": {\n"value": 0,\n"relation": "eq"\n}\n}\n}',
|
||||
},
|
||||
request: {
|
||||
data: '',
|
||||
method: 'GET',
|
||||
path: '_search',
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
timeMs: 22,
|
||||
statusCode: 400,
|
||||
statusText: 'Bad Request',
|
||||
contentType: 'application/json',
|
||||
value: '# GET _test 400 Bad Request\n{\n"error": {\n"root_cause": [],\n"status": 400\n}',
|
||||
},
|
||||
request: {
|
||||
data: '',
|
||||
method: 'GET',
|
||||
path: '_test',
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
timeMs: 23,
|
||||
// @ts-ignore
|
||||
statusCode: undefined,
|
||||
// @ts-ignore
|
||||
statusText: undefined,
|
||||
contentType: 'application/json',
|
||||
value: '# PUT /library/_bulk\n{\n"error": {\n"root_cause": [],\n"status": 500\n}',
|
||||
},
|
||||
request: {
|
||||
data: '',
|
||||
method: 'PUT',
|
||||
path: '/library/_bulk?refresh',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Only the second response has complete status code and text
|
||||
const EXPECTED_DECORATIONS = [
|
||||
{
|
||||
range: {
|
||||
endColumn: 28,
|
||||
endLineNumber: 12,
|
||||
startColumn: 13,
|
||||
startLineNumber: 12,
|
||||
},
|
||||
options: {
|
||||
inlineClassName: WARNING_STATUS_BADGE_CLASSNAME,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
expect(getStatusCodeDecorations(SAMPLE_INCOMPLETE_DATA)).toEqual(EXPECTED_DECORATIONS);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,28 +9,22 @@
|
|||
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import { RequestResult } from '../../../hooks/use_send_current_request/send_request';
|
||||
import {
|
||||
DEFAULT_STATUS_BADGE_CLASSNAME,
|
||||
SUCCESS_STATUS_BADGE_CLASSNAME,
|
||||
PRIMARY_STATUS_BADGE_CLASSNAME,
|
||||
WARNING_STATUS_BADGE_CLASSNAME,
|
||||
DANGER_STATUS_BADGE_CLASSNAME,
|
||||
} from './constants';
|
||||
import { STATUS_CODE_LINE_CLASSNAME } from './constants';
|
||||
|
||||
const getStatusCodeClassName = (statusCode: number) => {
|
||||
const getStatusCodeClassNameSuffix = (statusCode: number) => {
|
||||
if (statusCode <= 199) {
|
||||
return DEFAULT_STATUS_BADGE_CLASSNAME;
|
||||
return '--default';
|
||||
}
|
||||
if (statusCode <= 299) {
|
||||
return SUCCESS_STATUS_BADGE_CLASSNAME;
|
||||
return '--success';
|
||||
}
|
||||
if (statusCode <= 399) {
|
||||
return PRIMARY_STATUS_BADGE_CLASSNAME;
|
||||
return '--primary';
|
||||
}
|
||||
if (statusCode <= 499) {
|
||||
return WARNING_STATUS_BADGE_CLASSNAME;
|
||||
return '--warning';
|
||||
}
|
||||
return DANGER_STATUS_BADGE_CLASSNAME;
|
||||
return '--danger';
|
||||
};
|
||||
|
||||
export const getStatusCodeDecorations = (data: RequestResult[]) => {
|
||||
|
@ -39,25 +33,21 @@ export const getStatusCodeDecorations = (data: RequestResult[]) => {
|
|||
|
||||
data.forEach(({ response }) => {
|
||||
if (response?.value) {
|
||||
const totalStatus =
|
||||
response.statusCode && response.statusText
|
||||
? response.statusCode + ' ' + response.statusText
|
||||
: '';
|
||||
const startColumn = (response.value as string).indexOf(totalStatus) + 1;
|
||||
if (totalStatus && startColumn !== 0) {
|
||||
const range = {
|
||||
startLineNumber: lastResponseEndLine + 1,
|
||||
startColumn,
|
||||
endLineNumber: lastResponseEndLine + 1,
|
||||
endColumn: startColumn + totalStatus.length,
|
||||
};
|
||||
decorations.push({
|
||||
range,
|
||||
options: {
|
||||
inlineClassName: getStatusCodeClassName(response.statusCode),
|
||||
},
|
||||
});
|
||||
}
|
||||
const range = {
|
||||
startLineNumber: lastResponseEndLine + 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: lastResponseEndLine + 1,
|
||||
endColumn: 1, // It doesn't matter what endColumn we set as the decoration will be applied to the whole line
|
||||
};
|
||||
const classNameSuffix = getStatusCodeClassNameSuffix(response.statusCode);
|
||||
decorations.push({
|
||||
range,
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
blockClassName: `${STATUS_CODE_LINE_CLASSNAME}${classNameSuffix}`,
|
||||
marginClassName: `${STATUS_CODE_LINE_CLASSNAME}_number${classNameSuffix}`,
|
||||
},
|
||||
});
|
||||
lastResponseEndLine += (response.value as string).split(/\\n|\n/).length;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -130,7 +130,7 @@ export function sendRequest(args: RequestArgs): Promise<RequestResult[]> {
|
|||
|
||||
if (isMultiRequest) {
|
||||
const lineNumber = req.lineNumber ? `${req.lineNumber}: ` : '';
|
||||
value = `# ${lineNumber}${req.method} ${req.url} ${statusCode} ${statusText}\n${value}`;
|
||||
value = `# ${lineNumber}${req.method} ${req.url} [${statusCode} ${statusText}]\n${value}`;
|
||||
}
|
||||
|
||||
results.push({
|
||||
|
@ -164,7 +164,8 @@ export function sendRequest(args: RequestArgs): Promise<RequestResult[]> {
|
|||
}
|
||||
|
||||
if (isMultiRequest) {
|
||||
value = `# ${req.method} ${req.url} ${statusCode} ${statusText}\n${value}`;
|
||||
const lineNumber = req.lineNumber ? `${req.lineNumber}: ` : '';
|
||||
value = `# ${lineNumber}${req.method} ${req.url} [${statusCode} ${statusText}]\n${value}`;
|
||||
}
|
||||
|
||||
const result = {
|
||||
|
|
|
@ -203,29 +203,44 @@
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.monaco__status_badge--primary {
|
||||
@extend %monaco__status_badge;
|
||||
background-color: $euiColorVis1;
|
||||
.monaco__status_code_line--primary {
|
||||
background-color: transparentize($euiColorVis1, .9);
|
||||
}
|
||||
|
||||
.monaco__status_badge--success {
|
||||
@extend %monaco__status_badge;
|
||||
background-color: $euiColorSuccess;
|
||||
.monaco__status_code_line_number--primary {
|
||||
background-color: transparentize($euiColorVis1, .5);
|
||||
}
|
||||
|
||||
.monaco__status_badge--default {
|
||||
@extend %monaco__status_badge;
|
||||
background-color: $euiColorLightShade;
|
||||
.monaco__status_code_line--success {
|
||||
background-color: transparentize($euiColorSuccess, .9);
|
||||
}
|
||||
|
||||
.monaco__status_badge--warning {
|
||||
@extend %monaco__status_badge;
|
||||
background-color: $euiColorWarning;
|
||||
.monaco__status_code_line_number--success {
|
||||
background-color: transparentize($euiColorSuccess, .5);
|
||||
}
|
||||
|
||||
.monaco__status_badge--danger {
|
||||
@extend %monaco__status_badge;
|
||||
background-color: $euiColorDanger;
|
||||
.monaco__status_code_line--default {
|
||||
background-color: transparentize($euiColorLightShade, .9);
|
||||
}
|
||||
|
||||
.monaco__status_code_line_number--default {
|
||||
background-color: transparentize($euiColorLightShade, .5);
|
||||
}
|
||||
|
||||
.monaco__status_code_line--warning {
|
||||
background-color: transparentize($euiColorWarning, .9);
|
||||
}
|
||||
|
||||
.monaco__status_code_line_number--warning {
|
||||
background-color: transparentize($euiColorWarning, .5);
|
||||
}
|
||||
|
||||
.monaco__status_code_line--danger {
|
||||
background-color: transparentize($euiColorDanger, .9);
|
||||
}
|
||||
|
||||
.monaco__status_code_line_number--danger {
|
||||
background-color: transparentize($euiColorDanger, .5);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -203,8 +203,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await retry.try(async () => {
|
||||
const response = await PageObjects.console.getOutputText();
|
||||
log.debug(response);
|
||||
expect(response).to.contain('# 2: PUT test-index 200');
|
||||
expect(response).to.contain('# 3: DELETE test-index 200');
|
||||
expect(response).to.contain('# 2: PUT test-index [200 OK]');
|
||||
expect(response).to.contain('# 3: DELETE test-index [200 OK]');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await sendMultipleRequests(['\n GET /_search?pretty', '\n GET /_search?pretty']);
|
||||
|
||||
const response = await PageObjects.console.getOutputText();
|
||||
expect(response).to.contain('# 2: GET /_search?pretty');
|
||||
expect(response).to.contain('# 2: GET /_search?pretty [200 OK]');
|
||||
});
|
||||
|
||||
it('should clear the console output', async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue