Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Console] Improve status code highlighting #192888

Merged
merged 10 commits into from
Sep 18, 2024
27 changes: 15 additions & 12 deletions packages/kbn-monaco/src/console/lexer_rules/console_output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
],
},
};
50 changes: 42 additions & 8 deletions packages/kbn-monaco/src/console/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -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/;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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[]) => {
Expand All @@ -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;
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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 = {
Expand Down
Loading