Skip to content

Commit

Permalink
feat: display view query text
Browse files Browse the repository at this point in the history
  • Loading branch information
astandrik committed Dec 23, 2024
1 parent 37ccfb5 commit 14ca9f9
Show file tree
Hide file tree
Showing 10 changed files with 509 additions and 13 deletions.
280 changes: 279 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"react-redux": "^9.1.2",
"react-router-dom": "^5.3.4",
"react-split": "^2.0.14",
"react-syntax-highlighter": "^15.6.1",
"redux": "^5.0.1",
"redux-location-state": "^2.8.2",
"tslib": "^2.6.3",
Expand Down Expand Up @@ -136,6 +137,7 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@types/react-syntax-highlighter": "^15.5.13",
"@types/uuid": "^10.0.0",
"copyfiles": "^2.4.1",
"http-proxy-middleware": "^2.0.6",
Expand Down
1 change: 1 addition & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const config: PlaywrightTestConfig = {
: {
command: 'npm run dev',
port: 3000,
reuseExistingServer: true,
},
use: {
baseURL: baseUrl || 'http://localhost:3000/',
Expand Down
13 changes: 13 additions & 0 deletions src/components/SqlHighlighter/SqlHighlighter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.sql-highlighter {
code {
white-space: pre-wrap !important;

color: var(--g-color-text-primary) !important;
}

pre {
margin: 0 !important;

background: transparent !important;
}
}
30 changes: 30 additions & 0 deletions src/components/SqlHighlighter/SqlHighlighter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {useThemeValue} from '@gravity-ui/uikit';
import {PrismLight as SyntaxHighlighter} from 'react-syntax-highlighter';

import {cn} from '../../utils/cn';

import {dark, light, yql} from './yql';

import './SqlHighlighter.scss';

SyntaxHighlighter.registerLanguage('yql', yql);

const b = cn('sql-highlighter');

interface SqlHighlighterProps {
children: string;
className?: string;
}

export const SqlHighlighter = ({children, className}: SqlHighlighterProps) => {
const themeValue = useThemeValue();
const isDark = themeValue === 'dark' || themeValue === 'dark-hc';

return (
<div className={b(null, className)}>
<SyntaxHighlighter language="yql" style={isDark ? dark : light}>
{children}
</SyntaxHighlighter>
</div>
);
};
13 changes: 13 additions & 0 deletions src/components/SqlHighlighter/keywords.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const keywords =
'$row|$rows|action|add|all|alter|and|any|as|asc|assume|async|bigserial|serial|smallserial|serial8|serial4|serial2|begin|bernoulli|between|by|case|changefeed|column|columns|commit|compact|create|cross|cube|declare|define|delete|desc|dict|discard|distinct|do|drop|else|empty_action|end|erase|evaluate|exclusion|exists|export|family|flatten|for|from|full|global|group|grouping|having|if|ignore|ilike|import|in|index|inner|insert|into|is|join|key|left|like|limit|list|match|not|null|nulls|offset|on|only|optional|or|order|over|partition|pragma|presort|primary|process|reduce|regexp|repeatable|replace|respect|result|return|right|rlike|rollup|sample|schema|select|semi|set|sets|stream|subquery|sync|table|tablesample|then|truncate|union|intersect|except|update|upsert|use|using|values|view|when|where|window|with|without|xor|callable|resource|tagged|generic|unit|void|emptylist|emptydict|flow|callable|resource|tagged|generic|unit|void|emptylist|emptydict|flow'.split(
'|',
);
export const typeKeywords =
'bool|date|datetime|decimal|double|float|int16|int32|int64|int8|interval|json|jsondocument|string|timestamp|tzdate|tzdatetime|tztimestamp|uint16|uint32|uint64|uint8|utf8|uuid|yson|text|bytes'.split(
'|',
);

export const builtinFunctions =
'abs|aggregate_by|aggregate_list|aggregate_list_distinct|agg_list|agg_list_distinct|as_table|avg|avg_if|adaptivedistancehistogram|adaptivewardhistogram|adaptiveweighthistogram|addmember|addtimezone|aggregateflatten|aggregatetransforminput|aggregatetransformoutput|aggregationfactory|asatom|asdict|asdictstrict|asenum|aslist|asliststrict|asset|assetstrict|asstruct|astagged|astuple|asvariant|atomcode|bitcast|bit_and|bit_or|bit_xor|bool_and|bool_or|bool_xor|bottom|bottom_by|blockwardhistogram|blockweighthistogram|cast|coalesce|concat|concat_strict|correlation|count|count_if|covariance|covariance_population|covariance_sample|callableargument|callableargumenttype|callableresulttype|callabletype|callabletypecomponents|callabletypehandle|choosemembers|combinemembers|countdistinctestimate|currentauthenticateduser|currentoperationid|currentoperationsharedid|currenttzdate|currenttzdatetime|currenttztimestamp|currentutcdate|currentutcdatetime|currentutctimestamp|dense_rank|datatype|datatypecomponents|datatypehandle|dictaggregate|dictcontains|dictcreate|dicthasitems|dictitems|dictkeytype|dictkeys|dictlength|dictlookup|dictpayloadtype|dictpayloads|dicttype|dicttypecomponents|dicttypehandle|each|each_strict|emptydicttype|emptydicttypehandle|emptylisttype|emptylisttypehandle|endswith|ensure|ensureconvertibleto|ensuretype|enum|evaluateatom|evaluatecode|evaluateexpr|evaluatetype|expandstruct|filter|filter_strict|find|first_value|folder|filecontent|filepath|flattenmembers|forceremovemember|forceremovemembers|forcerenamemembers|forcespreadmembers|formatcode|formattype|frombytes|frompg|funccode|greatest|grouping|gathermembers|generictype|histogram|hll|hoppingwindowpgcast|hyperloglog|if|if_strict|instanceof|json_exists|json_query|json_value|jointablerow|just|lag|last_value|lead|least|len|length|like|likely|like_strict|lambdaargumentscount|lambdacode|lambdaoptionalargumentscount|linearhistogram|listaggregate|listall|listany|listavg|listcode|listcollect|listconcat|listcreate|listdistinct|listenumerate|listextend|listextendstrict|listextract|listfilter|listflatmap|listflatten|listfold|listfold1|listfold1map|listfoldmap|listfromrange|listfromtuple|listhas|listhasitems|listhead|listindexof|listitemtype|listlast|listlength|listmap|listmax|listmin|listnotnull|listreplicate|listreverse|listskip|listskipwhile|listskipwhileinclusive|listsort|listsortasc|listsortdesc|listsum|listtake|listtakewhile|listtakewhileinclusive|listtop|listtopsort|listtopasc|listtopdesc|listtopsortasc|listtopsortdesc|listtotuple|listtype|listtypehandle|listunionall|listuniq|listzip|listzipall|loghistogram|logarithmichistogram|max|max_by|max_of|median|min|min_by|min_of|mode|multi_aggregate_by|nanvl|nvl|nothing|nulltype|nulltypehandle|optionalitemtype|optionaltype|optionaltypehandle|percentile|parsefile|parsetype|parsetypehandle|pgand|pgarray|pgcall|pgconst|pgnot|pgop|pgor|pickle|quotecode|range|range_strict|rank|regexp|regexp_strict|rfind|row_number|random|randomnumber|randomuuid|removemember|removemembers|removetimezone|renamemembers|replacemember|reprcode|resourcetype|resourcetypehandle|resourcetypetag|some|stddev|stddev_population|stddev_sample|substring|sum|sum_if|sessionstart|sessionwindow|setcreate|setdifference|setincludes|setintersection|setisdisjoint|setsymmetricdifference|setunion|spreadmembers|stablepickle|startswith|staticmap|staticzip|streamitemtype|streamtype|streamtypehandle|structmembertype|structmembers|structtypecomponents|structtypehandle|subqueryextend|subqueryextendfor|subquerymerge|subquerymergefor|subqueryunionall|subqueryunionallfor|subqueryunionmerge|subqueryunionmergefor|top|topfreq|top_by|tablename|tablepath|tablerecordindex|tablerow|tablerows|taggedtype|taggedtypecomponents|taggedtypehandle|tobytes|todict|tomultidict|topg|toset|tosorteddict|tosortedmultidict|trymember|tupleelementtype|tupletype|tupletypecomponents|tupletypehandle|typehandle|typekind|typeof|udaf|udf|unittype|unpickle|untag|unwrap|variance|variance_population|variance_sample|variant|varianttype|varianttypehandle|variantunderlyingtype|voidtype|voidtypehandle|way|worldcode|weakfield'.split(
'|',
);
159 changes: 159 additions & 0 deletions src/components/SqlHighlighter/yql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
vscDarkPlus as darkTheme,
materialLight as lightTheme,
} from 'react-syntax-highlighter/dist/esm/styles/prism';

import {builtinFunctions, keywords, typeKeywords} from './keywords';

export const light = {
...lightTheme,
'pre[class*="language-"]': {
...lightTheme['pre[class*="language-"]'],
background: 'transparent',
},
'code[class*="language-"]': {
...lightTheme['code[class*="language-"]'],
background: 'transparent',
},
comment: {
color: '#969896',
},
string: {
color: '#a31515',
},
tablepath: {
color: '#338186',
},
function: {
color: '#7a3e9d',
},
udf: {
color: '#7a3e9d',
},
type: {
color: '#4d932d',
},
boolean: {
color: '#608b4e',
},
constant: {
color: '#608b4e',
},
variable: {
color: '#001188',
},
};

export const dark = {
...darkTheme,
'pre[class*="language-"]': {
...darkTheme['pre[class*="language-"]'],
background: 'transparent',
},
'code[class*="language-"]': {
...darkTheme['code[class*="language-"]'],
background: 'transparent',
},
comment: {
color: '#969896',
},
string: {
color: '#ce9178',
},
tablepath: {
color: '#338186',
},
function: {
color: '#9e7bb0',
},
udf: {
color: '#9e7bb0',
},
type: {
color: '#6A8759',
},
boolean: {
color: '#608b4e',
},
constant: {
color: '#608b4e',
},
variable: {
color: '#74b0df',
},
};

export function yql(Prism: any) {
// Define YQL language
Prism.languages.yql = {
comment: [
{
pattern: /--.*$/m,
greedy: true,
},
{
pattern: /\/\*[\s\S]*?(?:\*\/|$)/,
greedy: true,
},
],
tablepath: {
pattern: /(`[\w/]+`\s*\.\s*)?`[^`]+`/,
greedy: true,
},
string: [
{
pattern: /'(?:\\[\s\S]|[^\\'])*'/,
greedy: true,
},
{
pattern: /"(?:\\[\s\S]|[^\\"])*"/,
greedy: true,
},
{
pattern: /@@(?:[^@]|@(?!@))*@@/,
greedy: true,
},
],
variable: [
{
pattern: /\$[a-zA-Z_]\w*/,
greedy: true,
},
],
function: {
pattern: new RegExp(`\\b(?:${builtinFunctions.join('|')})\\b`, 'i'),
greedy: true,
},
keyword: {
pattern: new RegExp(`\\b(?:${keywords.join('|')})\\b`, 'i'),
greedy: true,
},
udf: {
pattern: /[A-Za-z_]\w*::[A-Za-z_]\w*/,
greedy: true,
},
type: {
pattern: new RegExp(`\\b(?:${typeKeywords.join('|')})\\b`, 'i'),
greedy: true,
},
boolean: {
pattern: /\b(?:true|false|null)\b/i,
greedy: true,
},
number: {
pattern: /[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,
greedy: true,
},
operator: {
pattern: /[-+*/%<>!=&|^~]+|\b(?:and|or|not|is|like|ilike|rlike|in|between)\b/i,
greedy: true,
},
punctuation: {
pattern: /[;[\](){}.,]/,
greedy: true,
},
};
}

yql.displayName = 'yql';
yql.aliases = ['yql'] as string[];
12 changes: 8 additions & 4 deletions src/components/TruncatedQuery/TruncatedQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import {cn} from '../../utils/cn';
import {CellWithPopover} from '../CellWithPopover/CellWithPopover';
import {SqlHighlighter} from '../SqlHighlighter/SqlHighlighter';

import './TruncatedQuery.scss';

Expand All @@ -22,12 +23,12 @@ export const TruncatedQuery = ({value = '', maxQueryHeight = 6}: TruncatedQueryP
'\n...\nThe request was truncated. Click on the line to show the full query on the query tab';
return (
<React.Fragment>
<span className={b()}>{content}</span>
<SqlHighlighter className={b()}>{content}</SqlHighlighter>
<span className={b('message', {color: 'secondary'})}>{message}</span>
</React.Fragment>
);
}
return <React.Fragment>{value}</React.Fragment>;
return <SqlHighlighter>{value}</SqlHighlighter>;
};

interface OneLineQueryWithPopoverProps {
Expand All @@ -36,8 +37,11 @@ interface OneLineQueryWithPopoverProps {

export const OneLineQueryWithPopover = ({value = ''}: OneLineQueryWithPopoverProps) => {
return (
<CellWithPopover contentClassName={b('popover-content')} content={value}>
{value}
<CellWithPopover
contentClassName={b('popover-content')}
content={<SqlHighlighter>{value}</SqlHighlighter>}
>
<SqlHighlighter>{value}</SqlHighlighter>
</CellWithPopover>
);
};
8 changes: 2 additions & 6 deletions src/containers/Tenant/Info/View/View.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {DefinitionListItem} from '@gravity-ui/components';
import {Text} from '@gravity-ui/uikit';

import {SqlHighlighter} from '../../../../components/SqlHighlighter/SqlHighlighter';
import {YDBDefinitionList} from '../../../../components/YDBDefinitionList/YDBDefinitionList';
import type {TEvDescribeSchemeResult} from '../../../../types/api/schema';
import {getEntityName} from '../../utils';
Expand All @@ -13,11 +13,7 @@ const prepareViewItems = (data: TEvDescribeSchemeResult): DefinitionListItem[] =
{
name: i18n('view.query-text'),
copyText: queryText,
content: (
<Text variant="code-2" wordBreak="break-word">
{queryText}
</Text>
),
content: queryText ? <SqlHighlighter>{queryText}</SqlHighlighter> : null,
},
];
};
Expand Down
4 changes: 2 additions & 2 deletions tests/suites/tenant/queryHistory/queryHistory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test.describe('Query History', () => {

// Check if the query appears in the history
const historyTable = page.locator('.ydb-queries-history table');
await expect(historyTable.locator(`text="${testQuery}"`)).toBeVisible({
await expect(historyTable.locator('.sql-highlighter', {hasText: testQuery})).toBeVisible({
timeout: VISIBILITY_TIMEOUT,
});
});
Expand Down Expand Up @@ -85,6 +85,6 @@ test.describe('Query History', () => {

// Check if the query appears in the history
const historyTable = page.locator('.ydb-queries-history table');
await expect(historyTable.locator(`text="${testQuery}"`)).toBeVisible();
await expect(historyTable.locator('.sql-highlighter', {hasText: testQuery})).toBeVisible();
});
});

0 comments on commit 14ca9f9

Please sign in to comment.