mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prettier had not been running as a precommit hook for some time so had to run in on all files again
This commit is contained in:
parent
3b0ae4bd0a
commit
cfea8bdcae
@ -30,7 +30,7 @@ export const warnAboutColorPickerPropsDeprecation = (componentName: string, prop
|
|||||||
|
|
||||||
export const colorPickerFactory = <T extends ColorPickerProps>(
|
export const colorPickerFactory = <T extends ColorPickerProps>(
|
||||||
popover: React.ComponentType<T>,
|
popover: React.ComponentType<T>,
|
||||||
displayName = 'ColorPicker',
|
displayName = 'ColorPicker'
|
||||||
) => {
|
) => {
|
||||||
return class ColorPicker extends Component<T, any> {
|
return class ColorPicker extends Component<T, any> {
|
||||||
static displayName = displayName;
|
static displayName = displayName;
|
||||||
|
@ -15,7 +15,7 @@ describe('ColorPickerPopover', () => {
|
|||||||
|
|
||||||
describe('rendering', () => {
|
describe('rendering', () => {
|
||||||
it('should render provided color as selected if color provided by name', () => {
|
it('should render provided color as selected if color provided by name', () => {
|
||||||
const wrapper = mount(<ColorPickerPopover color={BasicGreen.name} onChange={() => {}} theme={getTheme()}/>);
|
const wrapper = mount(<ColorPickerPopover color={BasicGreen.name} onChange={() => {}} theme={getTheme()} />);
|
||||||
const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
||||||
const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false);
|
const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false);
|
||||||
|
|
||||||
@ -25,7 +25,9 @@ describe('ColorPickerPopover', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should render provided color as selected if color provided by hex', () => {
|
it('should render provided color as selected if color provided by hex', () => {
|
||||||
const wrapper = mount(<ColorPickerPopover color={BasicGreen.variants.dark} onChange={() => {}} theme={getTheme()} />);
|
const wrapper = mount(
|
||||||
|
<ColorPickerPopover color={BasicGreen.variants.dark} onChange={() => {}} theme={getTheme()} />
|
||||||
|
);
|
||||||
const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
const selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
||||||
const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false);
|
const notSelectedSwatches = wrapper.find(ColorSwatch).filterWhere(node => node.prop('isSelected') === false);
|
||||||
|
|
||||||
@ -46,7 +48,11 @@ describe('ColorPickerPopover', () => {
|
|||||||
|
|
||||||
it('should pass hex color value to onChange prop by default', () => {
|
it('should pass hex color value to onChange prop by default', () => {
|
||||||
wrapper = mount(
|
wrapper = mount(
|
||||||
<ColorPickerPopover color={BasicGreen.variants.dark} onChange={onChangeSpy} theme={getTheme(GrafanaThemeType.Light)} />
|
<ColorPickerPopover
|
||||||
|
color={BasicGreen.variants.dark}
|
||||||
|
onChange={onChangeSpy}
|
||||||
|
theme={getTheme(GrafanaThemeType.Light)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
const basicBlueSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicBlue.name);
|
const basicBlueSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicBlue.name);
|
||||||
|
|
||||||
|
@ -119,4 +119,3 @@ export class ColorPickerPopover<T extends CustomPickersDescriptor> extends React
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import { getTheme } from '../../themes';
|
|||||||
import { GrafanaThemeType } from '../../types';
|
import { GrafanaThemeType } from '../../types';
|
||||||
|
|
||||||
describe('NamedColorsPalette', () => {
|
describe('NamedColorsPalette', () => {
|
||||||
|
|
||||||
const BasicGreen = getColorDefinitionByName('green');
|
const BasicGreen = getColorDefinitionByName('green');
|
||||||
|
|
||||||
describe('theme support for named colors', () => {
|
describe('theme support for named colors', () => {
|
||||||
@ -23,13 +22,15 @@ describe('NamedColorsPalette', () => {
|
|||||||
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark);
|
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark);
|
||||||
|
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
wrapper = mount(<NamedColorsPalette color={BasicGreen.name} theme={getTheme(GrafanaThemeType.Light)} onChange={() => {}} />);
|
wrapper = mount(
|
||||||
|
<NamedColorsPalette color={BasicGreen.name} theme={getTheme(GrafanaThemeType.Light)} onChange={() => {}} />
|
||||||
|
);
|
||||||
selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
||||||
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.light);
|
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.light);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render dar variant of provided color when theme not provided', () => {
|
it('should render dar variant of provided color when theme not provided', () => {
|
||||||
wrapper = mount(<NamedColorsPalette color={BasicGreen.name} onChange={() => {}} theme={getTheme()}/>);
|
wrapper = mount(<NamedColorsPalette color={BasicGreen.name} onChange={() => {}} theme={getTheme()} />);
|
||||||
selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
selectedSwatch = wrapper.find(ColorSwatch).findWhere(node => node.key() === BasicGreen.name);
|
||||||
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark);
|
expect(selectedSwatch.prop('color')).toBe(BasicGreen.variants.dark);
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,6 @@ const SpectrumPalettePointer: React.FunctionComponent<SpectrumPalettePointerProp
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const pointerColor = selectThemeVariant(
|
const pointerColor = selectThemeVariant(
|
||||||
{
|
{
|
||||||
light: theme.colors.dark3,
|
light: theme.colors.dark3,
|
||||||
|
@ -31,7 +31,7 @@ export const FormLabel: FunctionComponent<Props> = ({
|
|||||||
<label className={classes} {...rest} htmlFor={htmlFor}>
|
<label className={classes} {...rest} htmlFor={htmlFor}>
|
||||||
{children}
|
{children}
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
<Tooltip placement="top" content={tooltip} theme={"info"}>
|
<Tooltip placement="top" content={tooltip} theme={'info'}>
|
||||||
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
|
||||||
<i className="fa fa-info-circle" />
|
<i className="fa fa-info-circle" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,7 @@ const setup = (propOverrides?: object) => {
|
|||||||
width: 300,
|
width: 300,
|
||||||
value: 25,
|
value: 25,
|
||||||
decimals: 0,
|
decimals: 0,
|
||||||
theme: getTheme()
|
theme: getTheme(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(props, propOverrides);
|
Object.assign(props, propOverrides);
|
||||||
|
@ -6,10 +6,5 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PanelOptionsGrid: SFC<Props> = ({ children }) => {
|
export const PanelOptionsGrid: SFC<Props> = ({ children }) => {
|
||||||
|
return <div className="panel-options-grid">{children}</div>;
|
||||||
return (
|
|
||||||
<div className="panel-options-grid">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -61,7 +61,9 @@ interface AsyncProps {
|
|||||||
export const MenuList = (props: any) => {
|
export const MenuList = (props: any) => {
|
||||||
return (
|
return (
|
||||||
<components.MenuList {...props}>
|
<components.MenuList {...props}>
|
||||||
<CustomScrollbar autoHide={false} autoHeightMax="inherit">{props.children}</CustomScrollbar>
|
<CustomScrollbar autoHide={false} autoHeightMax="inherit">
|
||||||
|
{props.children}
|
||||||
|
</CustomScrollbar>
|
||||||
</components.MenuList>
|
</components.MenuList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,7 @@ export const Tooltip = ({ children, theme, ...controllerProps }: TooltipProps) =
|
|||||||
onMouseEnter={showPopper}
|
onMouseEnter={showPopper}
|
||||||
onMouseLeave={hidePopper}
|
onMouseLeave={hidePopper}
|
||||||
referenceElement={tooltipTriggerRef.current}
|
referenceElement={tooltipTriggerRef.current}
|
||||||
wrapperClassName='popper'
|
wrapperClassName="popper"
|
||||||
className={popperBackgroundClassName}
|
className={popperBackgroundClassName}
|
||||||
renderArrow={({ arrowProps, placement }) => (
|
renderArrow={({ arrowProps, placement }) => (
|
||||||
<div className="popper__arrow" data-placement={placement} {...arrowProps} />
|
<div className="popper__arrow" data-placement={placement} {...arrowProps} />
|
||||||
|
@ -7,4 +7,4 @@
|
|||||||
@import 'PanelOptionsGrid/PanelOptionsGrid';
|
@import 'PanelOptionsGrid/PanelOptionsGrid';
|
||||||
@import 'ColorPicker/ColorPicker';
|
@import 'ColorPicker/ColorPicker';
|
||||||
@import 'ValueMappingsEditor/ValueMappingsEditor';
|
@import 'ValueMappingsEditor/ValueMappingsEditor';
|
||||||
@import "FormField/FormField";
|
@import 'FormField/FormField';
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
name: 'Grafana Default',
|
name: 'Grafana Default',
|
||||||
typography: {
|
typography: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sansSerif: "'Roboto', Helvetica, Arial, sans-serif;",
|
sansSerif: "'Roboto', Helvetica, Arial, sans-serif;",
|
||||||
serif: "Georgia, 'Times New Roman', Times, serif;",
|
serif: "Georgia, 'Times New Roman', Times, serif;",
|
||||||
monospace: "Menlo, Monaco, Consolas, 'Courier New', monospace;"
|
monospace: "Menlo, Monaco, Consolas, 'Courier New', monospace;",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
base: '13px',
|
base: '13px',
|
||||||
@ -31,16 +29,16 @@ const theme = {
|
|||||||
lineHeight: {
|
lineHeight: {
|
||||||
xs: 1,
|
xs: 1,
|
||||||
s: 1.1,
|
s: 1.1,
|
||||||
m: 4/3,
|
m: 4 / 3,
|
||||||
l: 1.5
|
l: 1.5,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
brakpoints: {
|
brakpoints: {
|
||||||
xs: '0',
|
xs: '0',
|
||||||
s: '544px',
|
s: '544px',
|
||||||
m: '768px',
|
m: '768px',
|
||||||
l: '992px',
|
l: '992px',
|
||||||
xl: '1200px'
|
xl: '1200px',
|
||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
xs: '0',
|
xs: '0',
|
||||||
@ -55,8 +53,8 @@ const theme = {
|
|||||||
xs: '2px',
|
xs: '2px',
|
||||||
s: '3px',
|
s: '3px',
|
||||||
m: '5px',
|
m: '5px',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default theme;
|
export default theme;
|
||||||
|
@ -40,12 +40,10 @@ describe('Theme variable variant selector', () => {
|
|||||||
it('return dark theme variant if no theme given', () => {
|
it('return dark theme variant if no theme given', () => {
|
||||||
const theme = lightThemeMock;
|
const theme = lightThemeMock;
|
||||||
|
|
||||||
const selectedValue = selectThemeVariant(
|
const selectedValue = selectThemeVariant({
|
||||||
{
|
|
||||||
dark: theme.color.red,
|
dark: theme.color.red,
|
||||||
light: theme.color.green,
|
light: theme.color.green,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(selectedValue).toBe(lightThemeMock.color.red);
|
expect(selectedValue).toBe(lightThemeMock.color.red);
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { GrafanaThemeType } from '../types/theme';
|
import { GrafanaThemeType } from '../types/theme';
|
||||||
|
|
||||||
type VariantDescriptor = {
|
type VariantDescriptor = { [key in GrafanaThemeType]: string | number };
|
||||||
[key in GrafanaThemeType]: string | number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectThemeVariant = (variants: VariantDescriptor, currentTheme?: GrafanaThemeType) => {
|
export const selectThemeVariant = (variants: VariantDescriptor, currentTheme?: GrafanaThemeType) => {
|
||||||
return variants[currentTheme || GrafanaThemeType.Dark];
|
return variants[currentTheme || GrafanaThemeType.Dark];
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export * from './data';
|
export * from './data';
|
||||||
export * from './time';
|
export * from './time';
|
||||||
export * from './panel';
|
export * from './panel';
|
||||||
|
@ -45,7 +45,7 @@ describe('colors', () => {
|
|||||||
|
|
||||||
describe('getColorFromHexRgbOrName', () => {
|
describe('getColorFromHexRgbOrName', () => {
|
||||||
it('returns black for unknown color', () => {
|
it('returns black for unknown color', () => {
|
||||||
expect(getColorFromHexRgbOrName('aruba-sunshine')).toBe("#000000");
|
expect(getColorFromHexRgbOrName('aruba-sunshine')).toBe('#000000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns dark hex variant for known color if theme not specified', () => {
|
it('returns dark hex variant for known color if theme not specified', () => {
|
||||||
|
@ -70,7 +70,9 @@ export const getColorDefinitionByName = (name: Color): ColorDefinition => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getColorDefinition = (hex: string, theme: GrafanaThemeType): ColorDefinition | undefined => {
|
export const getColorDefinition = (hex: string, theme: GrafanaThemeType): ColorDefinition | undefined => {
|
||||||
return flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.variants[theme] === hex)[0];
|
return flatten(Array.from(getNamedColorPalette().values())).filter(
|
||||||
|
definition => definition.variants[theme] === hex
|
||||||
|
)[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const isHex = (color: string) => {
|
const isHex = (color: string) => {
|
||||||
@ -95,7 +97,9 @@ export const getColorName = (color?: string, theme?: GrafanaThemeType): Color |
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getColorByName = (colorName: string) => {
|
export const getColorByName = (colorName: string) => {
|
||||||
const definition = flatten(Array.from(getNamedColorPalette().values())).filter(definition => definition.name === colorName);
|
const definition = flatten(Array.from(getNamedColorPalette().values())).filter(
|
||||||
|
definition => definition.name === colorName
|
||||||
|
);
|
||||||
return definition.length > 0 ? definition[0] : undefined;
|
return definition.length > 0 ? definition[0] : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentValue !== null && typeof currentValue !== 'number') {
|
if (currentValue !== null && typeof currentValue !== 'number') {
|
||||||
throw {message: 'Time series contains non number values'};
|
throw { message: 'Time series contains non number values' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Due to missing values we could have different timeStep all along the series
|
// Due to missing values we could have different timeStep all along the series
|
||||||
|
@ -15,12 +15,7 @@ const ThemableStory: React.FunctionComponent<{}> = ({ children }) => {
|
|||||||
GrafanaThemeType.Dark
|
GrafanaThemeType.Dark
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return <ThemeContext.Provider value={getTheme(themeKnob)}>{children}</ThemeContext.Provider>;
|
||||||
<ThemeContext.Provider value={getTheme(themeKnob)}>
|
|
||||||
{children}
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Temporary solution. When we update to Storybook V5 we will be able to pass data from decorator to story
|
// Temporary solution. When we update to Storybook V5 we will be able to pass data from decorator to story
|
||||||
|
@ -306,7 +306,7 @@ export const getCategories = (): ValueFormatCategory[] => [
|
|||||||
{ name: 'kilometers/hour (km/h)', id: 'velocitykmh', fn: toFixedUnit('km/h') },
|
{ name: 'kilometers/hour (km/h)', id: 'velocitykmh', fn: toFixedUnit('km/h') },
|
||||||
{ name: 'miles/hour (mph)', id: 'velocitymph', fn: toFixedUnit('mph') },
|
{ name: 'miles/hour (mph)', id: 'velocitymph', fn: toFixedUnit('mph') },
|
||||||
{ name: 'knot (kn)', id: 'velocityknot', fn: toFixedUnit('kn') },
|
{ name: 'knot (kn)', id: 'velocityknot', fn: toFixedUnit('kn') },
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Volume',
|
name: 'Volume',
|
||||||
@ -318,5 +318,5 @@ export const getCategories = (): ValueFormatCategory[] => [
|
|||||||
{ name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') },
|
{ name: 'cubic decimetre', id: 'dm3', fn: toFixedUnit('dm³') },
|
||||||
{ name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') },
|
{ name: 'gallons', id: 'gallons', fn: toFixedUnit('gal') },
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
@ -27,7 +27,7 @@ class ErrorBoundary extends Component<Props, State> {
|
|||||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: error,
|
error: error,
|
||||||
errorInfo: errorInfo
|
errorInfo: errorInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ interface Props {
|
|||||||
newGrafanaVersion: string;
|
newGrafanaVersion: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Footer: FC<Props> = React.memo(({appName, buildVersion, buildCommit, newGrafanaVersionExists, newGrafanaVersion}) => {
|
export const Footer: FC<Props> = React.memo(
|
||||||
|
({ appName, buildVersion, buildCommit, newGrafanaVersionExists, newGrafanaVersion }) => {
|
||||||
return (
|
return (
|
||||||
<footer className="footer">
|
<footer className="footer">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
@ -30,7 +31,12 @@ export const Footer: FC<Props> = React.memo(({appName, buildVersion, buildCommit
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://grafana.com" target="_blank">{appName}</a> <span>v{buildVersion} (commit: {buildCommit})</span>
|
<a href="https://grafana.com" target="_blank">
|
||||||
|
{appName}
|
||||||
|
</a>{' '}
|
||||||
|
<span>
|
||||||
|
v{buildVersion} (commit: {buildCommit})
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{newGrafanaVersionExists && (
|
{newGrafanaVersionExists && (
|
||||||
<li>
|
<li>
|
||||||
@ -45,6 +51,7 @@ export const Footer: FC<Props> = React.memo(({appName, buildVersion, buildCommit
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default Footer;
|
export default Footer;
|
||||||
|
@ -33,9 +33,9 @@ class Page extends Component<Props> {
|
|||||||
updateTitle = () => {
|
updateTitle = () => {
|
||||||
const title = this.getPageTitle;
|
const title = this.getPageTitle;
|
||||||
document.title = title ? title + ' - Grafana' : 'Grafana';
|
document.title = title ? title + ' - Grafana' : 'Grafana';
|
||||||
}
|
};
|
||||||
|
|
||||||
get getPageTitle () {
|
get getPageTitle() {
|
||||||
const { navModel } = this.props;
|
const { navModel } = this.props;
|
||||||
if (navModel) {
|
if (navModel) {
|
||||||
return getTitleFromNavModel(navModel) || undefined;
|
return getTitleFromNavModel(navModel) || undefined;
|
||||||
@ -57,7 +57,8 @@ class Page extends Component<Props> {
|
|||||||
buildCommit={buildInfo.commit}
|
buildCommit={buildInfo.commit}
|
||||||
buildVersion={buildInfo.version}
|
buildVersion={buildInfo.version}
|
||||||
newGrafanaVersion={buildInfo.latestVersion}
|
newGrafanaVersion={buildInfo.latestVersion}
|
||||||
newGrafanaVersionExists={buildInfo.hasUpdate} />
|
newGrafanaVersionExists={buildInfo.hasUpdate}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PageContents extends Component<Props> {
|
class PageContents extends Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isLoading } = this.props;
|
const { isLoading } = this.props;
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ jest.mock('app/store/store', () => ({
|
|||||||
getState: jest.fn().mockReturnValue({
|
getState: jest.fn().mockReturnValue({
|
||||||
location: {
|
location: {
|
||||||
lastUpdated: 0,
|
lastUpdated: 0,
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('app/core/services/context_srv', () => ({
|
jest.mock('app/core/services/context_srv', () => ({
|
||||||
|
@ -57,7 +57,7 @@ export class Settings {
|
|||||||
isEnterprise: false,
|
isEnterprise: false,
|
||||||
},
|
},
|
||||||
viewersCanEdit: false,
|
viewersCanEdit: false,
|
||||||
disableSanitizeHtml: false
|
disableSanitizeHtml: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
_.extend(this, defaults, options);
|
_.extend(this, defaults, options);
|
||||||
|
@ -28,7 +28,7 @@ export function autofillEventFix($compile) {
|
|||||||
input.removeEventListener('animationstart', onAnimationStart);
|
input.removeEventListener('animationstart', onAnimationStart);
|
||||||
// input.removeEventListener('change', onChange);
|
// input.removeEventListener('change', onChange);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ export function dropdownTypeahead2($compile) {
|
|||||||
const $input = $(inputTemplate);
|
const $input = $(inputTemplate);
|
||||||
const $button = $(buttonTemplate);
|
const $button = $(buttonTemplate);
|
||||||
const timeoutId = {
|
const timeoutId = {
|
||||||
blur: null
|
blur: null,
|
||||||
};
|
};
|
||||||
$input.appendTo(elem);
|
$input.appendTo(elem);
|
||||||
$button.appendTo(elem);
|
$button.appendTo(elem);
|
||||||
|
@ -344,7 +344,7 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time
|
|||||||
datapoints: series.datapoints,
|
datapoints: series.datapoints,
|
||||||
target: series.alias,
|
target: series.alias,
|
||||||
alias: series.alias,
|
alias: series.alias,
|
||||||
color: series.color
|
color: series.color,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export class Profiler {
|
export class Profiler {
|
||||||
panelsRendered: number;
|
panelsRendered: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
@ -43,5 +43,5 @@ export function getNavModel(navIndex: NavIndex, id: string, fallback?: NavModel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getTitleFromNavModel = (navModel: NavModel) => {
|
export const getTitleFromNavModel = (navModel: NavModel) => {
|
||||||
return `${navModel.main.text}${navModel.node.text ? ': ' + navModel.node.text : '' }`;
|
return `${navModel.main.text}${navModel.node.text ? ': ' + navModel.node.text : ''}`;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
const backendSrv = {
|
const backendSrv = {
|
||||||
get: jest.fn(),
|
get: jest.fn(),
|
||||||
getDashboard: jest.fn(),
|
getDashboard: jest.fn(),
|
||||||
@ -10,5 +9,3 @@ const backendSrv = {
|
|||||||
export function getBackendSrv() {
|
export function getBackendSrv() {
|
||||||
return backendSrv;
|
return backendSrv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {shallow} from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
|
||||||
import {PasswordStrength} from '../components/PasswordStrength';
|
import { PasswordStrength } from '../components/PasswordStrength';
|
||||||
|
|
||||||
describe('PasswordStrength', () => {
|
describe('PasswordStrength', () => {
|
||||||
|
|
||||||
it('should have class bad if length below 4', () => {
|
it('should have class bad if length below 4', () => {
|
||||||
const wrapper = shallow(<PasswordStrength password="asd" />);
|
const wrapper = shallow(<PasswordStrength password="asd" />);
|
||||||
expect(wrapper.find(".password-strength-bad")).toHaveLength(1);
|
expect(wrapper.find('.password-strength-bad')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have class ok if length below 8', () => {
|
it('should have class ok if length below 8', () => {
|
||||||
const wrapper = shallow(<PasswordStrength password="asdasd" />);
|
const wrapper = shallow(<PasswordStrength password="asdasd" />);
|
||||||
expect(wrapper.find(".password-strength-ok")).toHaveLength(1);
|
expect(wrapper.find('.password-strength-ok')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have class good if length above 8', () => {
|
it('should have class good if length above 8', () => {
|
||||||
const wrapper = shallow(<PasswordStrength password="asdaasdda" />);
|
const wrapper = shallow(<PasswordStrength password="asdaasdda" />);
|
||||||
expect(wrapper.find(".password-strength-good")).toHaveLength(1);
|
expect(wrapper.find('.password-strength-good')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ const DEFAULT_EXPLORE_STATE: ExploreUrlState = {
|
|||||||
showingTable: true,
|
showingTable: true,
|
||||||
showingLogs: true,
|
showingLogs: true,
|
||||||
dedupStrategy: LogsDedupStrategy.none,
|
dedupStrategy: LogsDedupStrategy.none,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('state functions', () => {
|
describe('state functions', () => {
|
||||||
|
@ -207,7 +207,14 @@ export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: bo
|
|||||||
urlState.range.to,
|
urlState.range.to,
|
||||||
urlState.datasource,
|
urlState.datasource,
|
||||||
...urlState.queries,
|
...urlState.queries,
|
||||||
{ ui: [!!urlState.ui.showingGraph, !!urlState.ui.showingLogs, !!urlState.ui.showingTable, urlState.ui.dedupStrategy] },
|
{
|
||||||
|
ui: [
|
||||||
|
!!urlState.ui.showingGraph,
|
||||||
|
!!urlState.ui.showingLogs,
|
||||||
|
!!urlState.ui.showingTable,
|
||||||
|
urlState.ui.dedupStrategy,
|
||||||
|
],
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return JSON.stringify(urlState);
|
return JSON.stringify(urlState);
|
||||||
|
@ -289,11 +289,11 @@ kbn.getUnitFormats = () => {
|
|||||||
//
|
//
|
||||||
// Backward compatible layer for value formats to support old plugins
|
// Backward compatible layer for value formats to support old plugins
|
||||||
//
|
//
|
||||||
if (typeof Proxy !== "undefined") {
|
if (typeof Proxy !== 'undefined') {
|
||||||
kbn.valueFormats = new Proxy(kbn.valueFormats, {
|
kbn.valueFormats = new Proxy(kbn.valueFormats, {
|
||||||
get(target, name, receiver) {
|
get(target, name, receiver) {
|
||||||
if (typeof name !== 'string') {
|
if (typeof name !== 'string') {
|
||||||
throw {message: `Value format ${String(name)} is not a string` };
|
throw { message: `Value format ${String(name)} is not a string` };
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatter = getValueFormat(name);
|
const formatter = getValueFormat(name);
|
||||||
@ -303,7 +303,7 @@ if (typeof Proxy !== "undefined") {
|
|||||||
|
|
||||||
// default to look here
|
// default to look here
|
||||||
return Reflect.get(target, name, receiver);
|
return Reflect.get(target, name, receiver);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
kbn.valueFormats = getValueFormatterIndex();
|
kbn.valueFormats = getValueFormatterIndex();
|
||||||
|
@ -15,7 +15,7 @@ export default function getScrollbarWidth() {
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '-9999px',
|
top: '-9999px',
|
||||||
overflow: 'scroll',
|
overflow: 'scroll',
|
||||||
MsOverflowStyle: 'scrollbar'
|
MsOverflowStyle: 'scrollbar',
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(newStyles).map(style => {
|
Object.keys(newStyles).map(style => {
|
||||||
@ -23,7 +23,7 @@ export default function getScrollbarWidth() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
scrollbarWidth = (div.offsetWidth - div.clientWidth);
|
scrollbarWidth = div.offsetWidth - div.clientWidth;
|
||||||
document.body.removeChild(div);
|
document.body.removeChild(div);
|
||||||
} else {
|
} else {
|
||||||
scrollbarWidth = 0;
|
scrollbarWidth = 0;
|
||||||
|
@ -50,7 +50,7 @@ const XSSWL = Object.keys(xss.whiteList).reduce((acc, element) => {
|
|||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const sanitizeXSS = new xss.FilterXSS({
|
const sanitizeXSS = new xss.FilterXSS({
|
||||||
whiteList: XSSWL
|
whiteList: XSSWL,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +60,7 @@ const sanitizeXSS = new xss.FilterXSS({
|
|||||||
* Info: https://github.com/leizongmin/js-xss#customize-css-filter
|
* Info: https://github.com/leizongmin/js-xss#customize-css-filter
|
||||||
* Whitelist: https://github.com/leizongmin/js-css-filter/blob/master/lib/default.js
|
* Whitelist: https://github.com/leizongmin/js-css-filter/blob/master/lib/default.js
|
||||||
*/
|
*/
|
||||||
export function sanitize (unsanitizedString: string): string {
|
export function sanitize(unsanitizedString: string): string {
|
||||||
try {
|
try {
|
||||||
return sanitizeXSS.process(unsanitizedString);
|
return sanitizeXSS.process(unsanitizedString);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -12,13 +12,13 @@ export function renderUrl(path: string, query: UrlQueryMap | undefined): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function encodeURIComponentAsAngularJS(val, pctEncodeSpaces) {
|
export function encodeURIComponentAsAngularJS(val, pctEncodeSpaces) {
|
||||||
return encodeURIComponent(val).
|
return encodeURIComponent(val)
|
||||||
replace(/%40/gi, '@').
|
.replace(/%40/gi, '@')
|
||||||
replace(/%3A/gi, ':').
|
.replace(/%3A/gi, ':')
|
||||||
replace(/%24/g, '$').
|
.replace(/%24/g, '$')
|
||||||
replace(/%2C/gi, ',').
|
.replace(/%2C/gi, ',')
|
||||||
replace(/%3B/gi, ';').
|
.replace(/%3B/gi, ';')
|
||||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
.replace(/%20/g, pctEncodeSpaces ? '%20' : '+');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toUrlParams(a) {
|
export function toUrlParams(a) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default class AdminEditOrgCtrl {
|
export default class AdminEditOrgCtrl {
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
|
constructor($scope, $routeParams, backendSrv, $location, navModelSrv) {
|
||||||
@ -46,4 +45,3 @@ export default class AdminEditOrgCtrl {
|
|||||||
$scope.init();
|
$scope.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export default class AdminListOrgsCtrl {
|
export default class AdminListOrgsCtrl {
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, backendSrv, navModelSrv) {
|
constructor($scope, backendSrv, navModelSrv) {
|
||||||
@ -31,4 +30,3 @@ export default class AdminListOrgsCtrl {
|
|||||||
$scope.init();
|
$scope.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ export class ServerStats extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
stats: [],
|
stats: [],
|
||||||
isLoading: false
|
isLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,4 +25,3 @@ export default class StyleGuideCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ export class AlertTabCtrl {
|
|||||||
name: model.name,
|
name: model.name,
|
||||||
iconClass: this.getNotificationIcon(model.type),
|
iconClass: this.getNotificationIcon(model.type),
|
||||||
isDefault: false,
|
isDefault: false,
|
||||||
uid: model.uid
|
uid: model.uid,
|
||||||
});
|
});
|
||||||
|
|
||||||
// avoid duplicates using both id and uid to be backwards compatible.
|
// avoid duplicates using both id and uid to be backwards compatible.
|
||||||
|
@ -10,13 +10,7 @@ export class AdHocFiltersCtrl {
|
|||||||
removeTagFilterSegment: any;
|
removeTagFilterSegment: any;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(
|
constructor(private uiSegmentSrv, private datasourceSrv, private $q, private variableSrv, $scope) {
|
||||||
private uiSegmentSrv,
|
|
||||||
private datasourceSrv,
|
|
||||||
private $q,
|
|
||||||
private variableSrv,
|
|
||||||
$scope,
|
|
||||||
) {
|
|
||||||
this.removeTagFilterSegment = uiSegmentSrv.newSegment({
|
this.removeTagFilterSegment = uiSegmentSrv.newSegment({
|
||||||
fake: true,
|
fake: true,
|
||||||
value: '-- remove filter --',
|
value: '-- remove filter --',
|
||||||
|
@ -161,7 +161,9 @@ export class AddPanelWidget extends React.Component<Props, State> {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="add-panel-widget__actions">
|
<div className="add-panel-widget__actions">
|
||||||
<button className="btn btn-inverse add-panel-widget__action" onClick={this.onCreateNewRow}>Convert to row</button>
|
<button className="btn btn-inverse add-panel-widget__action" onClick={this.onCreateNewRow}>
|
||||||
|
Convert to row
|
||||||
|
</button>
|
||||||
{copiedPanelPlugins.length === 1 && (
|
{copiedPanelPlugins.length === 1 && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-inverse add-panel-widget__action"
|
className="btn btn-inverse add-panel-widget__action"
|
||||||
|
@ -76,7 +76,9 @@ export class DashboardPermissions extends PureComponent<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
<button className="btn btn-primary pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}>Add Permission</button>
|
<button className="btn btn-primary pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}>
|
||||||
|
Add Permission
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SlideDown in={isAdding}>
|
<SlideDown in={isAdding}>
|
||||||
|
@ -27,7 +27,7 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
|||||||
|
|
||||||
onVariableUpdated = () => {
|
onVariableUpdated = () => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
};
|
||||||
|
|
||||||
onToggle = () => {
|
onToggle = () => {
|
||||||
this.props.dashboard.toggleRow(this.props.panel);
|
this.props.dashboard.toggleRow(this.props.panel);
|
||||||
@ -35,12 +35,12 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
|||||||
this.setState(prevState => {
|
this.setState(prevState => {
|
||||||
return { collapsed: !prevState.collapsed };
|
return { collapsed: !prevState.collapsed };
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
onUpdate = () => {
|
onUpdate = () => {
|
||||||
this.props.dashboard.processRepeats();
|
this.props.dashboard.processRepeats();
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
};
|
||||||
|
|
||||||
onOpenSettings = () => {
|
onOpenSettings = () => {
|
||||||
appEvents.emit('show-modal', {
|
appEvents.emit('show-modal', {
|
||||||
@ -51,7 +51,7 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
|||||||
onUpdated: this.onUpdate,
|
onUpdated: this.onUpdate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
onDelete = () => {
|
onDelete = () => {
|
||||||
appEvents.emit('confirm-modal', {
|
appEvents.emit('confirm-modal', {
|
||||||
@ -66,7 +66,7 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
|
|||||||
this.props.dashboard.removeRow(this.props.panel, false);
|
this.props.dashboard.removeRow(this.props.panel, false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
|
@ -31,6 +31,6 @@ export class DashboardSettings extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div className="panel-height-helper" ref={element => this.element = element} />;
|
return <div className="panel-height-helper" ref={element => (this.element = element)} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,6 @@ export class SubMenu extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div ref={element => this.element = element} />;
|
return <div ref={element => (this.element = element)} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ interface ScenarioContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel {
|
function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel {
|
||||||
const data = Object.assign({
|
const data = Object.assign(
|
||||||
|
{
|
||||||
title: 'My dashboard',
|
title: 'My dashboard',
|
||||||
panels: [
|
panels: [
|
||||||
{
|
{
|
||||||
@ -33,7 +34,9 @@ function getTestDashboard(overrides?: any, metaOverrides?: any): DashboardModel
|
|||||||
gridPos: { x: 0, y: 0, w: 1, h: 1 },
|
gridPos: { x: 0, y: 0, w: 1, h: 1 },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}, overrides);
|
},
|
||||||
|
overrides
|
||||||
|
);
|
||||||
|
|
||||||
const meta = Object.assign({ canSave: true, canEdit: true }, metaOverrides);
|
const meta = Object.assign({ canSave: true, canEdit: true }, metaOverrides);
|
||||||
return new DashboardModel(data, meta);
|
return new DashboardModel(data, meta);
|
||||||
@ -74,7 +77,7 @@ function dashboardPageScenario(description, scenarioFn: (ctx: ScenarioContext) =
|
|||||||
|
|
||||||
ctx.dashboard = props.dashboard;
|
ctx.dashboard = props.dashboard;
|
||||||
ctx.wrapper = shallow(<DashboardPage {...props} />);
|
ctx.wrapper = shallow(<DashboardPage {...props} />);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -86,8 +89,7 @@ function dashboardPageScenario(description, scenarioFn: (ctx: ScenarioContext) =
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('DashboardPage', () => {
|
describe('DashboardPage', () => {
|
||||||
|
dashboardPageScenario('Given initial state', ctx => {
|
||||||
dashboardPageScenario("Given initial state", (ctx) => {
|
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
});
|
});
|
||||||
@ -97,7 +99,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("Dashboard is fetching slowly", (ctx) => {
|
dashboardPageScenario('Dashboard is fetching slowly', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.wrapper.setProps({
|
ctx.wrapper.setProps({
|
||||||
@ -111,7 +113,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("Dashboard init completed ", (ctx) => {
|
dashboardPageScenario('Dashboard init completed ', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp();
|
ctx.setDashboardProp();
|
||||||
@ -126,7 +128,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("When user goes into panel edit", (ctx) => {
|
dashboardPageScenario('When user goes into panel edit', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp();
|
ctx.setDashboardProp();
|
||||||
@ -149,7 +151,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("When user goes back to dashboard from panel edit", (ctx) => {
|
dashboardPageScenario('When user goes back to dashboard from panel edit', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp();
|
ctx.setDashboardProp();
|
||||||
@ -179,7 +181,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("When dashboard has editview url state", (ctx) => {
|
dashboardPageScenario('When dashboard has editview url state', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp();
|
ctx.setDashboardProp();
|
||||||
@ -197,7 +199,7 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("When adding panel", (ctx) => {
|
dashboardPageScenario('When adding panel', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp();
|
ctx.setDashboardProp();
|
||||||
@ -214,37 +216,37 @@ describe('DashboardPage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("Given panel with id 0", (ctx) => {
|
dashboardPageScenario('Given panel with id 0', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp({
|
ctx.setDashboardProp({
|
||||||
panels: [{ id: 0, type: 'graph'}],
|
panels: [{ id: 0, type: 'graph' }],
|
||||||
schemaVersion: 17,
|
schemaVersion: 17,
|
||||||
});
|
});
|
||||||
ctx.wrapper.setProps({
|
ctx.wrapper.setProps({
|
||||||
urlEdit: true,
|
urlEdit: true,
|
||||||
urlFullscreen: true,
|
urlFullscreen: true,
|
||||||
urlPanelId: '0'
|
urlPanelId: '0',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should go into edit mode' , () => {
|
it('Should go into edit mode', () => {
|
||||||
expect(ctx.wrapper.state().isEditing).toBe(true);
|
expect(ctx.wrapper.state().isEditing).toBe(true);
|
||||||
expect(ctx.wrapper.state().fullscreenPanel.id).toBe(0);
|
expect(ctx.wrapper.state().fullscreenPanel.id).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dashboardPageScenario("When dashboard unmounts", (ctx) => {
|
dashboardPageScenario('When dashboard unmounts', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.mount();
|
ctx.mount();
|
||||||
ctx.setDashboardProp({
|
ctx.setDashboardProp({
|
||||||
panels: [{ id: 0, type: 'graph'}],
|
panels: [{ id: 0, type: 'graph' }],
|
||||||
schemaVersion: 17,
|
schemaVersion: 17,
|
||||||
});
|
});
|
||||||
ctx.wrapper.unmount();
|
ctx.wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should call clean up action' , () => {
|
it('Should call clean up action', () => {
|
||||||
expect(ctx.cleanUpDashboardMock.calls).toBe(1);
|
expect(ctx.cleanUpDashboardMock.calls).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ let lastGridWidth = 1200;
|
|||||||
let ignoreNextWidthChange = false;
|
let ignoreNextWidthChange = false;
|
||||||
|
|
||||||
interface GridWrapperProps {
|
interface GridWrapperProps {
|
||||||
size: { width: number; };
|
size: { width: number };
|
||||||
layout: ReactGridLayout.Layout[];
|
layout: ReactGridLayout.Layout[];
|
||||||
onLayoutChange: (layout: ReactGridLayout.Layout[]) => void;
|
onLayoutChange: (layout: ReactGridLayout.Layout[]) => void;
|
||||||
children: JSX.Element | JSX.Element[];
|
children: JSX.Element | JSX.Element[];
|
||||||
@ -149,21 +149,21 @@ export class DashboardGrid extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.props.dashboard.sortPanelsByGridPos();
|
this.props.dashboard.sortPanelsByGridPos();
|
||||||
}
|
};
|
||||||
|
|
||||||
triggerForceUpdate = () => {
|
triggerForceUpdate = () => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
};
|
||||||
|
|
||||||
onWidthChange = () => {
|
onWidthChange = () => {
|
||||||
for (const panel of this.props.dashboard.panels) {
|
for (const panel of this.props.dashboard.panels) {
|
||||||
panel.resizeDone();
|
panel.resizeDone();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onViewModeChanged = () => {
|
onViewModeChanged = () => {
|
||||||
ignoreNextWidthChange = true;
|
ignoreNextWidthChange = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => {
|
updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => {
|
||||||
this.panelMap[item.i].updateGridPos(item);
|
this.panelMap[item.i].updateGridPos(item);
|
||||||
@ -171,21 +171,21 @@ export class DashboardGrid extends PureComponent<Props> {
|
|||||||
// react-grid-layout has a bug (#670), and onLayoutChange() is only called when the component is mounted.
|
// react-grid-layout has a bug (#670), and onLayoutChange() is only called when the component is mounted.
|
||||||
// So it's required to call it explicitly when panel resized or moved to save layout changes.
|
// So it's required to call it explicitly when panel resized or moved to save layout changes.
|
||||||
this.onLayoutChange(layout);
|
this.onLayoutChange(layout);
|
||||||
}
|
};
|
||||||
|
|
||||||
onResize: ItemCallback = (layout, oldItem, newItem) => {
|
onResize: ItemCallback = (layout, oldItem, newItem) => {
|
||||||
console.log();
|
console.log();
|
||||||
this.panelMap[newItem.i].updateGridPos(newItem);
|
this.panelMap[newItem.i].updateGridPos(newItem);
|
||||||
}
|
};
|
||||||
|
|
||||||
onResizeStop: ItemCallback = (layout, oldItem, newItem) => {
|
onResizeStop: ItemCallback = (layout, oldItem, newItem) => {
|
||||||
this.updateGridPos(newItem, layout);
|
this.updateGridPos(newItem, layout);
|
||||||
this.panelMap[newItem.i].resizeDone();
|
this.panelMap[newItem.i].resizeDone();
|
||||||
}
|
};
|
||||||
|
|
||||||
onDragStop: ItemCallback = (layout, oldItem, newItem) => {
|
onDragStop: ItemCallback = (layout, oldItem, newItem) => {
|
||||||
this.updateGridPos(newItem, layout);
|
this.updateGridPos(newItem, layout);
|
||||||
}
|
};
|
||||||
|
|
||||||
renderPanels() {
|
renderPanels() {
|
||||||
const panelElements = [];
|
const panelElements = [];
|
||||||
|
@ -30,18 +30,18 @@ interface State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PanelHeader extends Component<Props, State> {
|
export class PanelHeader extends Component<Props, State> {
|
||||||
clickCoordinates: ClickCoordinates = {x: 0, y: 0};
|
clickCoordinates: ClickCoordinates = { x: 0, y: 0 };
|
||||||
state = {
|
state = {
|
||||||
panelMenuOpen: false,
|
panelMenuOpen: false,
|
||||||
clickCoordinates: {x: 0, y: 0}
|
clickCoordinates: { x: 0, y: 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
eventToClickCoordinates = (event: React.MouseEvent<HTMLDivElement>) => {
|
eventToClickCoordinates = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
return {
|
return {
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
y: event.clientY
|
y: event.clientY,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
onMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
|
onMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
this.clickCoordinates = this.eventToClickCoordinates(event);
|
this.clickCoordinates = this.eventToClickCoordinates(event);
|
||||||
@ -49,7 +49,7 @@ export class PanelHeader extends Component<Props, State> {
|
|||||||
|
|
||||||
isClick = (clickCoordinates: ClickCoordinates) => {
|
isClick = (clickCoordinates: ClickCoordinates) => {
|
||||||
return isEqual(clickCoordinates, this.clickCoordinates);
|
return isEqual(clickCoordinates, this.clickCoordinates);
|
||||||
}
|
};
|
||||||
|
|
||||||
onMenuToggle = (event: React.MouseEvent<HTMLDivElement>) => {
|
onMenuToggle = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (this.isClick(this.eventToClickCoordinates(event))) {
|
if (this.isClick(this.eventToClickCoordinates(event))) {
|
||||||
|
@ -76,13 +76,8 @@ export class PanelHeaderCorner extends Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{infoMode === InfoModes.Info || infoMode === InfoModes.Links ? (
|
{infoMode === InfoModes.Info || infoMode === InfoModes.Links ? (
|
||||||
<Tooltip
|
<Tooltip content={this.getInfoContent()} placement="bottom-start">
|
||||||
content={this.getInfoContent()}
|
<div className={`panel-info-corner panel-info-corner--${infoMode.toLowerCase()}`}>
|
||||||
placement="bottom-start"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`panel-info-corner panel-info-corner--${infoMode.toLowerCase()}`}
|
|
||||||
>
|
|
||||||
<i className="fa" />
|
<i className="fa" />
|
||||||
<span className="panel-info-corner-inner" />
|
<span className="panel-info-corner-inner" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,7 +66,7 @@ export class PanelResizer extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{render(isEditing ? {height: editorHeight} : this.noStyles)}
|
{render(isEditing ? { height: editorHeight } : this.noStyles)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
<div className="panel-editor-container__resizer">
|
<div className="panel-editor-container__resizer">
|
||||||
<Draggable axis="y" grid={[100, 1]} onDrag={this.onDrag} position={{ x: 0, y: 0 }}>
|
<Draggable axis="y" grid={[100, 1]} onDrag={this.onDrag} position={{ x: 0, y: 0 }}>
|
||||||
|
@ -27,4 +27,3 @@ import DashboardPermissions from './components/DashboardPermissions/DashboardPer
|
|||||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
|
|
||||||
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['dashboardId', 'folder']);
|
react2AngularDirective('dashboardPermissions', DashboardPermissions, ['dashboardId', 'folder']);
|
||||||
|
|
||||||
|
@ -131,14 +131,7 @@ export class QueryEditorRow extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
if (datasource.pluginExports.QueryEditor) {
|
if (datasource.pluginExports.QueryEditor) {
|
||||||
const QueryEditor = datasource.pluginExports.QueryEditor;
|
const QueryEditor = datasource.pluginExports.QueryEditor;
|
||||||
return (
|
return <QueryEditor query={query} datasource={datasource} onChange={onChange} onRunQuery={this.onRunQuery} />;
|
||||||
<QueryEditor
|
|
||||||
query={query}
|
|
||||||
datasource={datasource}
|
|
||||||
onChange={onChange}
|
|
||||||
onRunQuery={this.onRunQuery}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>Data source plugin does not export any Query Editor component</div>;
|
return <div>Data source plugin does not export any Query Editor component</div>;
|
||||||
|
@ -31,7 +31,7 @@ export class DashboardSrv {
|
|||||||
removePanel(dashboard, dashboard.getPanelById(panelId), true);
|
removePanel(dashboard, dashboard.getPanelById(panelId), true);
|
||||||
};
|
};
|
||||||
|
|
||||||
onPanelChangeView = (options) => {
|
onPanelChangeView = options => {
|
||||||
const urlParams = this.$location.search();
|
const urlParams = this.$location.search();
|
||||||
|
|
||||||
// handle toggle logic
|
// handle toggle logic
|
||||||
|
@ -917,5 +917,4 @@ export class DashboardModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,7 @@ describe('PanelModel', () => {
|
|||||||
model = new PanelModel({
|
model = new PanelModel({
|
||||||
type: 'table',
|
type: 'table',
|
||||||
showColumns: true,
|
showColumns: true,
|
||||||
targets: [
|
targets: [{ refId: 'A' }, { noRefId: true }],
|
||||||
{refId: 'A'},
|
|
||||||
{noRefId: true}
|
|
||||||
]
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -139,4 +139,3 @@ export function removeDashboard(uri: string): ThunkResult<void> {
|
|||||||
dispatch(loadPluginDashboards());
|
dispatch(loadPluginDashboards());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,7 @@ import thunk from 'redux-thunk';
|
|||||||
import { initDashboard, InitDashboardArgs } from './initDashboard';
|
import { initDashboard, InitDashboardArgs } from './initDashboard';
|
||||||
import { DashboardRouteInfo } from 'app/types';
|
import { DashboardRouteInfo } from 'app/types';
|
||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
import {
|
import { dashboardInitFetching, dashboardInitCompleted, dashboardInitServices } from './actions';
|
||||||
dashboardInitFetching,
|
|
||||||
dashboardInitCompleted,
|
|
||||||
dashboardInitServices,
|
|
||||||
} from './actions';
|
|
||||||
|
|
||||||
jest.mock('app/core/services/backend_srv');
|
jest.mock('app/core/services/backend_srv');
|
||||||
|
|
||||||
@ -138,9 +134,11 @@ describeInitScenario('Initializing new dashboard', ctx => {
|
|||||||
describeInitScenario('Initializing home dashboard', ctx => {
|
describeInitScenario('Initializing home dashboard', ctx => {
|
||||||
ctx.setup(() => {
|
ctx.setup(() => {
|
||||||
ctx.args.routeInfo = DashboardRouteInfo.Home;
|
ctx.args.routeInfo = DashboardRouteInfo.Home;
|
||||||
ctx.backendSrv.get.mockReturnValue(Promise.resolve({
|
ctx.backendSrv.get.mockReturnValue(
|
||||||
redirectUri: '/u/123/my-home'
|
Promise.resolve({
|
||||||
}));
|
redirectUri: '/u/123/my-home',
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should redirect to custom home dashboard', () => {
|
it('Should redirect to custom home dashboard', () => {
|
||||||
@ -148,5 +146,3 @@ describeInitScenario('Initializing home dashboard', ctx => {
|
|||||||
expect(ctx.actions[1].payload.path).toBe('/u/123/my-home');
|
expect(ctx.actions[1].payload.path).toBe('/u/123/my-home');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe('dashboard reducer', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
state = dashboardReducer(initialState, dashboardInitFetching());
|
state = dashboardReducer(initialState, dashboardInitFetching());
|
||||||
state = dashboardReducer(state, dashboardInitFailed({message: 'Oh no', error: 'sad'}));
|
state = dashboardReducer(state, dashboardInitFailed({ message: 'Oh no', error: 'sad' }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set model', async () => {
|
it('should set model', async () => {
|
||||||
|
@ -70,7 +70,6 @@ export const dashboardReducer = reducerFactory(initialState)
|
|||||||
.addMapper({
|
.addMapper({
|
||||||
filter: cleanUpDashboard,
|
filter: cleanUpDashboard,
|
||||||
mapper: (state, action) => {
|
mapper: (state, action) => {
|
||||||
|
|
||||||
// Destroy current DashboardModel
|
// Destroy current DashboardModel
|
||||||
// Very important as this removes all dashboard event listeners
|
// Very important as this removes all dashboard event listeners
|
||||||
state.model.destroy();
|
state.model.destroy();
|
||||||
|
@ -39,7 +39,7 @@ describe('applyPanelTimeOverrides', () => {
|
|||||||
|
|
||||||
it('should apply time shift', () => {
|
it('should apply time shift', () => {
|
||||||
const panelModel = {
|
const panelModel = {
|
||||||
timeShift: '2h'
|
timeShift: '2h',
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
|
const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
|
||||||
@ -57,7 +57,7 @@ describe('applyPanelTimeOverrides', () => {
|
|||||||
it('should apply both relative time and time shift', () => {
|
it('should apply both relative time and time shift', () => {
|
||||||
const panelModel = {
|
const panelModel = {
|
||||||
timeFrom: '2h',
|
timeFrom: '2h',
|
||||||
timeShift: '2h'
|
timeShift: '2h',
|
||||||
};
|
};
|
||||||
|
|
||||||
const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
|
const expectedFromDate = moment([2019, 1, 11, 10, 0, 0]).toDate();
|
||||||
|
@ -176,11 +176,11 @@ export const snapshotDataToPanelData = (panel: PanelModel): PanelData => {
|
|||||||
const snapshotData = panel.snapshotData;
|
const snapshotData = panel.snapshotData;
|
||||||
if (isTimeSeries(snapshotData[0])) {
|
if (isTimeSeries(snapshotData[0])) {
|
||||||
return {
|
return {
|
||||||
timeSeries: snapshotData
|
timeSeries: snapshotData,
|
||||||
} as PanelData;
|
} as PanelData;
|
||||||
} else if (isTableData(snapshotData[0])) {
|
} else if (isTableData(snapshotData[0])) {
|
||||||
return {
|
return {
|
||||||
tableData: snapshotData[0]
|
tableData: snapshotData[0],
|
||||||
} as PanelData;
|
} as PanelData;
|
||||||
}
|
}
|
||||||
throw new Error('snapshotData is invalid:' + snapshotData.toString());
|
throw new Error('snapshotData is invalid:' + snapshotData.toString());
|
||||||
|
@ -14,7 +14,7 @@ const setup = (propOverrides?: object) => {
|
|||||||
loadDataSource: jest.fn(),
|
loadDataSource: jest.fn(),
|
||||||
loadPluginDashboards: jest.fn(),
|
loadPluginDashboards: jest.fn(),
|
||||||
removeDashboard: jest.fn(),
|
removeDashboard: jest.fn(),
|
||||||
isLoading: false
|
isLoading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.assign(props, propOverrides);
|
Object.assign(props, propOverrides);
|
||||||
|
@ -74,7 +74,6 @@ export class DataSourceDashboards extends PureComponent<Props> {
|
|||||||
onImport={(dashboard, overwrite) => this.onImport(dashboard, overwrite)}
|
onImport={(dashboard, overwrite) => this.onImport(dashboard, overwrite)}
|
||||||
onRemove={dashboard => this.onRemove(dashboard)}
|
onRemove={dashboard => this.onRemove(dashboard)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</Page.Contents>
|
</Page.Contents>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
@ -88,7 +87,7 @@ function mapStateToProps(state: StoreState) {
|
|||||||
pageId: pageId,
|
pageId: pageId,
|
||||||
dashboards: state.plugins.dashboards,
|
dashboards: state.plugins.dashboards,
|
||||||
dataSource: getDataSource(state.dataSources, pageId),
|
dataSource: getDataSource(state.dataSources, pageId),
|
||||||
isLoading: state.plugins.isLoadingPluginDashboards
|
isLoading: state.plugins.isLoadingPluginDashboards,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,4 +38,3 @@ export default class CreateFolderCtrl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,4 +23,3 @@ export default class FolderDashboardsCtrl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => {
|
|||||||
organization: {} as Organization,
|
organization: {} as Organization,
|
||||||
navModel: {
|
navModel: {
|
||||||
main: {
|
main: {
|
||||||
text: 'Configuration'
|
text: 'Configuration',
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
text: 'Org details'
|
text: 'Org details',
|
||||||
}
|
},
|
||||||
} as NavModel,
|
} as NavModel,
|
||||||
loadOrganization: jest.fn(),
|
loadOrganization: jest.fn(),
|
||||||
setOrganizationName: jest.fn(),
|
setOrganizationName: jest.fn(),
|
||||||
|
@ -8,11 +8,11 @@ const setup = (propOverrides?: object) => {
|
|||||||
const props: Props = {
|
const props: Props = {
|
||||||
navModel: {
|
navModel: {
|
||||||
main: {
|
main: {
|
||||||
text: 'Configuration'
|
text: 'Configuration',
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
text: 'Plugins'
|
text: 'Plugins',
|
||||||
}
|
},
|
||||||
} as NavModel,
|
} as NavModel,
|
||||||
plugins: [] as Plugin[],
|
plugins: [] as Plugin[],
|
||||||
searchQuery: '',
|
searchQuery: '',
|
||||||
|
@ -57,9 +57,7 @@ export class PluginListPage extends PureComponent<Props> {
|
|||||||
setSearchQuery={query => setPluginsSearchQuery(query)}
|
setSearchQuery={query => setPluginsSearchQuery(query)}
|
||||||
linkButton={linkButton}
|
linkButton={linkButton}
|
||||||
/>
|
/>
|
||||||
{hasFetched && plugins && (
|
{hasFetched && plugins && (plugins && <PluginList plugins={plugins} layoutMode={layoutMode} />)}
|
||||||
plugins && <PluginList plugins={plugins} layoutMode={layoutMode} />
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
</Page.Contents>
|
</Page.Contents>
|
||||||
</Page>
|
</Page>
|
||||||
|
@ -60,7 +60,8 @@ const pluginDashboardsLoaded = (dashboards: PluginDashboard[]): LoadedPluginDash
|
|||||||
payload: dashboards,
|
payload: dashboards,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Action = LoadPluginsAction
|
export type Action =
|
||||||
|
| LoadPluginsAction
|
||||||
| LoadPluginDashboardsAction
|
| LoadPluginDashboardsAction
|
||||||
| LoadedPluginDashboardsAction
|
| LoadedPluginDashboardsAction
|
||||||
| SetPluginsSearchQueryAction
|
| SetPluginsSearchQueryAction
|
||||||
|
@ -9,7 +9,7 @@ export const initialState: PluginsState = {
|
|||||||
layoutMode: LayoutModes.Grid,
|
layoutMode: LayoutModes.Grid,
|
||||||
hasFetched: false,
|
hasFetched: false,
|
||||||
dashboards: [] as PluginDashboard[],
|
dashboards: [] as PluginDashboard[],
|
||||||
isLoadingPluginDashboards: false
|
isLoadingPluginDashboards: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pluginsReducer = (state = initialState, action: Action): PluginsState => {
|
export const pluginsReducer = (state = initialState, action: Action): PluginsState => {
|
||||||
|
@ -473,7 +473,7 @@ describe('templateSrv', () => {
|
|||||||
type: 'custom',
|
type: 'custom',
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
current: { value: 'constructor', text: 'constructor' },
|
current: { value: 'constructor', text: 'constructor' },
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
_templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
|
_templateSrv.setGrafanaVariable('$__auto_interval_interval', '13m');
|
||||||
_templateSrv.updateIndex();
|
_templateSrv.updateIndex();
|
||||||
|
@ -47,13 +47,7 @@ describe('VariableSrv', function(this: any) {
|
|||||||
const ds: any = {};
|
const ds: any = {};
|
||||||
ds.metricFindQuery = () => Promise.resolve(scenario.queryResult);
|
ds.metricFindQuery = () => Promise.resolve(scenario.queryResult);
|
||||||
|
|
||||||
ctx.variableSrv = new VariableSrv(
|
ctx.variableSrv = new VariableSrv($q, ctx.$location, ctx.$injector, ctx.templateSrv, ctx.timeSrv);
|
||||||
$q,
|
|
||||||
ctx.$location,
|
|
||||||
ctx.$injector,
|
|
||||||
ctx.templateSrv,
|
|
||||||
ctx.timeSrv
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.variableSrv.timeSrv = ctx.timeSrv;
|
ctx.variableSrv.timeSrv = ctx.timeSrv;
|
||||||
ctx.datasourceSrv = {
|
ctx.datasourceSrv = {
|
||||||
|
@ -18,13 +18,13 @@ export class VariableSrv {
|
|||||||
variables: any[];
|
variables: any[];
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(private $q,
|
constructor(
|
||||||
|
private $q,
|
||||||
private $location,
|
private $location,
|
||||||
private $injector,
|
private $injector,
|
||||||
private templateSrv: TemplateSrv,
|
private templateSrv: TemplateSrv,
|
||||||
private timeSrv: TimeSrv) {
|
private timeSrv: TimeSrv
|
||||||
|
) {}
|
||||||
}
|
|
||||||
|
|
||||||
init(dashboard: DashboardModel) {
|
init(dashboard: DashboardModel) {
|
||||||
this.dashboard = dashboard;
|
this.dashboard = dashboard;
|
||||||
|
@ -13,11 +13,11 @@ const setup = (propOverrides?: object) => {
|
|||||||
const props: Props = {
|
const props: Props = {
|
||||||
navModel: {
|
navModel: {
|
||||||
main: {
|
main: {
|
||||||
text: 'Configuration'
|
text: 'Configuration',
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
text: 'Users'
|
text: 'Users',
|
||||||
}
|
},
|
||||||
} as NavModel,
|
} as NavModel,
|
||||||
users: [] as OrgUser[],
|
users: [] as OrgUser[],
|
||||||
invitees: [] as Invitee[],
|
invitees: [] as Invitee[],
|
||||||
|
@ -301,7 +301,6 @@ export default class CloudWatchDatasource {
|
|||||||
return this.getEc2InstanceAttribute(region, targetAttributeName, filterJson);
|
return this.getEc2InstanceAttribute(region, targetAttributeName, filterJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const resourceARNsQuery = query.match(/^resource_arns\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);
|
const resourceARNsQuery = query.match(/^resource_arns\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/);
|
||||||
if (resourceARNsQuery) {
|
if (resourceARNsQuery) {
|
||||||
region = resourceARNsQuery[1];
|
region = resourceARNsQuery[1];
|
||||||
|
@ -385,12 +385,16 @@ describe('CloudWatchDatasource', () => {
|
|||||||
scenario.requestResponse = {
|
scenario.requestResponse = {
|
||||||
results: {
|
results: {
|
||||||
metricFindQuery: {
|
metricFindQuery: {
|
||||||
tables: [{
|
tables: [
|
||||||
rows: [[
|
{
|
||||||
|
rows: [
|
||||||
|
[
|
||||||
'arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567',
|
'arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567',
|
||||||
'arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321'
|
'arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321',
|
||||||
]]
|
],
|
||||||
}],
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ describe('ElasticDatasource', function(this: any) {
|
|||||||
|
|
||||||
const templateSrv = {
|
const templateSrv = {
|
||||||
replace: jest.fn(text => {
|
replace: jest.fn(text => {
|
||||||
if (text.startsWith("$")) {
|
if (text.startsWith('$')) {
|
||||||
return `resolvedVariable`;
|
return `resolvedVariable`;
|
||||||
} else {
|
} else {
|
||||||
return text;
|
return text;
|
||||||
@ -94,7 +94,7 @@ describe('ElasticDatasource', function(this: any) {
|
|||||||
},
|
},
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
alias: "$varAlias",
|
alias: '$varAlias',
|
||||||
bucketAggs: [],
|
bucketAggs: [],
|
||||||
metrics: [{ type: 'raw_document' }],
|
metrics: [{ type: 'raw_document' }],
|
||||||
query: 'escape\\:test',
|
query: 'escape\\:test',
|
||||||
|
@ -202,7 +202,7 @@ export default class ResponseParser {
|
|||||||
parseQuerySchema() {
|
parseQuerySchema() {
|
||||||
const result = {
|
const result = {
|
||||||
Type: 'AppInsights',
|
Type: 'AppInsights',
|
||||||
Tables: {}
|
Tables: {},
|
||||||
};
|
};
|
||||||
if (this.results && this.results.data && this.results.data.Tables) {
|
if (this.results && this.results.data && this.results.data.Tables) {
|
||||||
for (let i = 0; i < this.results.data.Tables[0].Rows.length; i++) {
|
for (let i = 0; i < this.results.data.Tables[0].Rows.length; i++) {
|
||||||
@ -215,9 +215,7 @@ export default class ResponseParser {
|
|||||||
} else {
|
} else {
|
||||||
result.Tables[columnTable] = {
|
result.Tables[columnTable] = {
|
||||||
Name: columnTable,
|
Name: columnTable,
|
||||||
OrderedColumns: [
|
OrderedColumns: [{ Name: columnName, Type: columnType }],
|
||||||
{ Name: columnName, Type: columnType }
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import { getNextCharacter } from 'app/features/explore/utils/dom';
|
|||||||
import { KEYWORDS, functionTokens, operatorTokens, grafanaMacros } from './kusto/kusto';
|
import { KEYWORDS, functionTokens, operatorTokens, grafanaMacros } from './kusto/kusto';
|
||||||
// import '../sass/editor.base.scss';
|
// import '../sass/editor.base.scss';
|
||||||
|
|
||||||
|
|
||||||
const TYPEAHEAD_DELAY = 100;
|
const TYPEAHEAD_DELAY = 100;
|
||||||
|
|
||||||
interface Suggestion {
|
interface Suggestion {
|
||||||
@ -41,8 +40,8 @@ interface KustoDBSchema {
|
|||||||
|
|
||||||
const defaultSchema = () => ({
|
const defaultSchema = () => ({
|
||||||
Databases: {
|
Databases: {
|
||||||
Default: {}
|
Default: {},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
||||||
@ -171,7 +170,8 @@ export default class KustoQueryField extends QueryField {
|
|||||||
|
|
||||||
let results = 0;
|
let results = 0;
|
||||||
prefix = prefix.toLowerCase();
|
prefix = prefix.toLowerCase();
|
||||||
const filteredSuggestions = suggestionGroups.map(group => {
|
const filteredSuggestions = suggestionGroups
|
||||||
|
.map(group => {
|
||||||
if (group.items && prefix && !group.skipFilter) {
|
if (group.items && prefix && !group.skipFilter) {
|
||||||
group.items = group.items.filter(c => c.text.length >= prefix.length);
|
group.items = group.items.filter(c => c.text.length >= prefix.length);
|
||||||
if (group.prefixMatch) {
|
if (group.prefixMatch) {
|
||||||
@ -195,7 +195,7 @@ export default class KustoQueryField extends QueryField {
|
|||||||
suggestions: results > 0 ? filteredSuggestions : [],
|
suggestions: results > 0 ? filteredSuggestions : [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
applyTypeahead(change, suggestion) {
|
applyTypeahead(change, suggestion) {
|
||||||
const { typeaheadPrefix, typeaheadContext, typeaheadText } = this.state;
|
const { typeaheadPrefix, typeaheadContext, typeaheadText } = this.state;
|
||||||
@ -301,28 +301,34 @@ export default class KustoQueryField extends QueryField {
|
|||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Keywords',
|
label: 'Keywords',
|
||||||
items: KEYWORDS.map(wrapText)
|
items: KEYWORDS.map(wrapText),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Operators',
|
label: 'Operators',
|
||||||
items: operatorTokens
|
items: operatorTokens,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Functions',
|
label: 'Functions',
|
||||||
items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
|
items: functionTokens.map((s: any) => {
|
||||||
|
s.type = 'function';
|
||||||
|
return s;
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Macros',
|
label: 'Macros',
|
||||||
items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
items: grafanaMacros.map((s: any) => {
|
||||||
|
s.type = 'function';
|
||||||
|
return s;
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Tables',
|
label: 'Tables',
|
||||||
items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name }))
|
items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name })),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,13 +337,19 @@ export default class KustoQueryField extends QueryField {
|
|||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Functions',
|
label: 'Functions',
|
||||||
items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
|
items: functionTokens.map((s: any) => {
|
||||||
|
s.type = 'function';
|
||||||
|
return s;
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Macros',
|
label: 'Macros',
|
||||||
items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
items: grafanaMacros.map((s: any) => {
|
||||||
}
|
s.type = 'function';
|
||||||
|
return s;
|
||||||
|
}),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,8 +359,8 @@ export default class KustoQueryField extends QueryField {
|
|||||||
{
|
{
|
||||||
prefixMatch: true,
|
prefixMatch: true,
|
||||||
label: 'Tables',
|
label: 'Tables',
|
||||||
items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name }))
|
items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name })),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -366,9 +378,9 @@ export default class KustoQueryField extends QueryField {
|
|||||||
label: 'Fields',
|
label: 'Fields',
|
||||||
items: _.map(tableSchema.OrderedColumns, (f: any) => ({
|
items: _.map(tableSchema.OrderedColumns, (f: any) => ({
|
||||||
text: f.Name,
|
text: f.Name,
|
||||||
hint: f.Type
|
hint: f.Type,
|
||||||
}))
|
})),
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ interface EditorProps {
|
|||||||
|
|
||||||
class Editor extends Component<EditorProps, any> {
|
class Editor extends Component<EditorProps, any> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
placeholder: 'Enter a query'
|
placeholder: 'Enter a query',
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -68,8 +68,13 @@ coreModule.directive('kustoEditor', [
|
|||||||
'reactDirective',
|
'reactDirective',
|
||||||
reactDirective => {
|
reactDirective => {
|
||||||
return reactDirective(Editor, [
|
return reactDirective(Editor, [
|
||||||
'change', 'database', 'execute', 'query', 'variables', 'placeholder',
|
'change',
|
||||||
['getSchema', { watchDepth: 'reference' }]
|
'database',
|
||||||
|
'execute',
|
||||||
|
'query',
|
||||||
|
'variables',
|
||||||
|
'placeholder',
|
||||||
|
['getSchema', { watchDepth: 'reference' }],
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -1,272 +1,619 @@
|
|||||||
/* tslint:disable:max-line-length */
|
/* tslint:disable:max-line-length */
|
||||||
export const operatorTokens = [
|
export const operatorTokens = [
|
||||||
{ text: "!between", hint: "Matches the input that is outside the inclusive range." },
|
{ text: '!between', hint: 'Matches the input that is outside the inclusive range.' },
|
||||||
{ text: "as", hint: "Binds a name to the operator's input tabular expression." },
|
{ text: 'as', hint: "Binds a name to the operator's input tabular expression." },
|
||||||
{ text: "between", hint: "Matches the input that is inside the inclusive range." },
|
{ text: 'between', hint: 'Matches the input that is inside the inclusive range.' },
|
||||||
{ text: "consume", hint: "The `consume` operator consumes the tabular data stream handed to it. It is\r\nmostly used for triggering the query side-effect without actually returning\r\nthe results back to the caller." },
|
{
|
||||||
{ text: "count", hint: "Returns the number of records in the input record set." },
|
text: 'consume',
|
||||||
{ text: "datatable", hint: "Returns a table whose schema and values are defined in the query itself." },
|
hint:
|
||||||
{ text: "distinct", hint: "Produces a table with the distinct combination of the provided columns of the input table." },
|
'The `consume` operator consumes the tabular data stream handed to it. It is\r\nmostly used for triggering the query side-effect without actually returning\r\nthe results back to the caller.',
|
||||||
{ text: "evaluate", hint: "Invokes a service-side query extension (plugin)." },
|
},
|
||||||
{ text: "extend", hint: "Create calculated columns and append them to the result set." },
|
{ text: 'count', hint: 'Returns the number of records in the input record set.' },
|
||||||
{ text: "externaldata", hint: "Returns a table whose schema is defined in the query itself, and whose data is read from an external raw file." },
|
{ text: 'datatable', hint: 'Returns a table whose schema and values are defined in the query itself.' },
|
||||||
{ text: "facet", hint: "Returns a set of tables, one for each specified column.\r\nEach table specifies the list of values taken by its column.\r\nAn additional table can be created by using the `with` clause." },
|
{
|
||||||
{ text: "find", hint: "Finds rows that match a predicate across a set of tables." },
|
text: 'distinct',
|
||||||
{ text: "fork", hint: "Runs multiple consumer operators in parallel." },
|
hint: 'Produces a table with the distinct combination of the provided columns of the input table.',
|
||||||
{ text: "getschema", hint: "Produce a table that represents a tabular schema of the input." },
|
},
|
||||||
{ text: "in", hint: "Filters a recordset based on the provided set of values." },
|
{ text: 'evaluate', hint: 'Invokes a service-side query extension (plugin).' },
|
||||||
{ text: "invoke", hint: "Invokes lambda that receives the source of `invoke` as tabular parameter argument." },
|
{ text: 'extend', hint: 'Create calculated columns and append them to the result set.' },
|
||||||
{ text: "join", hint: "Merge the rows of two tables to form a new table by matching values of the specified column(s) from each table." },
|
{
|
||||||
{ text: "limit", hint: "Return up to the specified number of rows." },
|
text: 'externaldata',
|
||||||
{ text: "make-series", hint: "Create series of specified aggregated values along specified axis." },
|
hint:
|
||||||
{ text: "mvexpand", hint: "Expands multi-value array or property bag." },
|
'Returns a table whose schema is defined in the query itself, and whose data is read from an external raw file.',
|
||||||
{ text: "order", hint: "Sort the rows of the input table into order by one or more columns." },
|
},
|
||||||
{ text: "parse", hint: "Evaluates a string expression and parses its value into one or more calculated columns." },
|
{
|
||||||
{ text: "print", hint: "Evaluates one or more scalar expressions and inserts the results (as a single-row table with as many columns as there are expressions) into the output." },
|
text: 'facet',
|
||||||
{ text: "project", hint: "Select the columns to include, rename or drop, and insert new computed columns." },
|
hint:
|
||||||
{ text: "project-away", hint: "Select what columns to exclude from the input." },
|
'Returns a set of tables, one for each specified column.\r\nEach table specifies the list of values taken by its column.\r\nAn additional table can be created by using the `with` clause.',
|
||||||
{ text: "project-rename", hint: "Renames columns in the result output." },
|
},
|
||||||
{ text: "range", hint: "Generates a single-column table of values." },
|
{ text: 'find', hint: 'Finds rows that match a predicate across a set of tables.' },
|
||||||
{ text: "reduce", hint: "Groups a set of strings together based on values similarity." },
|
{ text: 'fork', hint: 'Runs multiple consumer operators in parallel.' },
|
||||||
{ text: "render", hint: "Instructs the user agent to render the results of the query in a particular way." },
|
{ text: 'getschema', hint: 'Produce a table that represents a tabular schema of the input.' },
|
||||||
{ text: "sample", hint: "Returns up to the specified number of random rows from the input table." },
|
{ text: 'in', hint: 'Filters a recordset based on the provided set of values.' },
|
||||||
{ text: "sample-distinct", hint: "Returns a single column that contains up to the specified number of distinct values of the requested column." },
|
{ text: 'invoke', hint: 'Invokes lambda that receives the source of `invoke` as tabular parameter argument.' },
|
||||||
{ text: "search", hint: "The search operator provides a multi-table/multi-column search experience." },
|
{
|
||||||
{ text: "serialize", hint: "Marks that order of the input row set is safe for window functions usage." },
|
text: 'join',
|
||||||
{ text: "sort", hint: "Sort the rows of the input table into order by one or more columns." },
|
hint:
|
||||||
{ text: "summarize", hint: "Produces a table that aggregates the content of the input table." },
|
'Merge the rows of two tables to form a new table by matching values of the specified column(s) from each table.',
|
||||||
{ text: "take", hint: "Return up to the specified number of rows." },
|
},
|
||||||
{ text: "top", hint: "Returns the first *N* records sorted by the specified columns." },
|
{ text: 'limit', hint: 'Return up to the specified number of rows.' },
|
||||||
{ text: "top-hitters", hint: "Returns an approximation of the first *N* results (assuming skewed distribution of the input)." },
|
{ text: 'make-series', hint: 'Create series of specified aggregated values along specified axis.' },
|
||||||
{ text: "top-nested", hint: "Produces hierarchical top results, where each level is a drill-down based on previous level values." },
|
{ text: 'mvexpand', hint: 'Expands multi-value array or property bag.' },
|
||||||
{ text: "union", hint: "Takes two or more tables and returns the rows of all of them." },
|
{ text: 'order', hint: 'Sort the rows of the input table into order by one or more columns.' },
|
||||||
{ text: "where", hint: "Filters a table to the subset of rows that satisfy a predicate." },
|
{ text: 'parse', hint: 'Evaluates a string expression and parses its value into one or more calculated columns.' },
|
||||||
|
{
|
||||||
|
text: 'print',
|
||||||
|
hint:
|
||||||
|
'Evaluates one or more scalar expressions and inserts the results (as a single-row table with as many columns as there are expressions) into the output.',
|
||||||
|
},
|
||||||
|
{ text: 'project', hint: 'Select the columns to include, rename or drop, and insert new computed columns.' },
|
||||||
|
{ text: 'project-away', hint: 'Select what columns to exclude from the input.' },
|
||||||
|
{ text: 'project-rename', hint: 'Renames columns in the result output.' },
|
||||||
|
{ text: 'range', hint: 'Generates a single-column table of values.' },
|
||||||
|
{ text: 'reduce', hint: 'Groups a set of strings together based on values similarity.' },
|
||||||
|
{ text: 'render', hint: 'Instructs the user agent to render the results of the query in a particular way.' },
|
||||||
|
{ text: 'sample', hint: 'Returns up to the specified number of random rows from the input table.' },
|
||||||
|
{
|
||||||
|
text: 'sample-distinct',
|
||||||
|
hint:
|
||||||
|
'Returns a single column that contains up to the specified number of distinct values of the requested column.',
|
||||||
|
},
|
||||||
|
{ text: 'search', hint: 'The search operator provides a multi-table/multi-column search experience.' },
|
||||||
|
{ text: 'serialize', hint: 'Marks that order of the input row set is safe for window functions usage.' },
|
||||||
|
{ text: 'sort', hint: 'Sort the rows of the input table into order by one or more columns.' },
|
||||||
|
{ text: 'summarize', hint: 'Produces a table that aggregates the content of the input table.' },
|
||||||
|
{ text: 'take', hint: 'Return up to the specified number of rows.' },
|
||||||
|
{ text: 'top', hint: 'Returns the first *N* records sorted by the specified columns.' },
|
||||||
|
{
|
||||||
|
text: 'top-hitters',
|
||||||
|
hint: 'Returns an approximation of the first *N* results (assuming skewed distribution of the input).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'top-nested',
|
||||||
|
hint: 'Produces hierarchical top results, where each level is a drill-down based on previous level values.',
|
||||||
|
},
|
||||||
|
{ text: 'union', hint: 'Takes two or more tables and returns the rows of all of them.' },
|
||||||
|
{ text: 'where', hint: 'Filters a table to the subset of rows that satisfy a predicate.' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const functionTokens = [
|
export const functionTokens = [
|
||||||
{ text: "abs", hint: "Calculates the absolute value of the input." },
|
{ text: 'abs', hint: 'Calculates the absolute value of the input.' },
|
||||||
{ text: "acos", hint: "Returns the angle whose cosine is the specified number (the inverse operation of [`cos()`](cosfunction.md)) ." },
|
{
|
||||||
{ text: "ago", hint: "Subtracts the given timespan from the current UTC clock time." },
|
text: 'acos',
|
||||||
{ text: "any", hint: "Returns random non-empty value from the specified expression values." },
|
hint:
|
||||||
{ text: "arg_max", hint: "Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row)." },
|
'Returns the angle whose cosine is the specified number (the inverse operation of [`cos()`](cosfunction.md)) .',
|
||||||
{ text: "arg_min", hint: "Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row)." },
|
},
|
||||||
{ text: "argmax", hint: "Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row)." },
|
{ text: 'ago', hint: 'Subtracts the given timespan from the current UTC clock time.' },
|
||||||
{ text: "argmin", hint: "Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row)." },
|
{ text: 'any', hint: 'Returns random non-empty value from the specified expression values.' },
|
||||||
{ text: "array_concat", hint: "Concatenates a number of dynamic arrays to a single array." },
|
{
|
||||||
{ text: "array_length", hint: "Calculates the number of elements in a dynamic array." },
|
text: 'arg_max',
|
||||||
{ text: "array_slice", hint: "Extracts a slice of a dynamic array." },
|
hint:
|
||||||
{ text: "array_split", hint: "Splits an array to multiple arrays according to the split indices and packs the generated array in a dynamic array." },
|
'Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).',
|
||||||
{ text: "asin", hint: "Returns the angle whose sine is the specified number (the inverse operation of [`sin()`](sinfunction.md)) ." },
|
},
|
||||||
{ text: "assert", hint: "Checks for a condition; if the condition is false, outputs error messages and fails the query." },
|
{
|
||||||
{ text: "atan", hint: "Returns the angle whose tangent is the specified number (the inverse operation of [`tan()`](tanfunction.md)) ." },
|
text: 'arg_min',
|
||||||
{ text: "atan2", hint: "Calculates the angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x)." },
|
hint:
|
||||||
{ text: "avg", hint: "Calculates the average of *Expr* across the group." },
|
'Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).',
|
||||||
{ text: "avgif", hint: "Calculates the [average](avg-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`." },
|
},
|
||||||
{ text: "bag_keys", hint: "Enumerates all the root keys in a dynamic property-bag object." },
|
{
|
||||||
{ text: "base64_decodestring", hint: "Decodes a base64 string to a UTF-8 string" },
|
text: 'argmax',
|
||||||
{ text: "base64_encodestring", hint: "Encodes a string as base64 string" },
|
hint:
|
||||||
{ text: "beta_cdf", hint: "Returns the standard cumulative beta distribution function." },
|
'Finds a row in the group that maximizes *ExprToMaximize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).',
|
||||||
{ text: "beta_inv", hint: "Returns the inverse of the beta cumulative probability beta density function." },
|
},
|
||||||
{ text: "beta_pdf", hint: "Returns the probability density beta function." },
|
{
|
||||||
{ text: "bin", hint: "Rounds values down to an integer multiple of a given bin size." },
|
text: 'argmin',
|
||||||
{ text: "bin_at", hint: "Rounds values down to a fixed-size \'bin\', with control over the bin's starting point.\r\n(See also [`bin function`](./binfunction.md).)" },
|
hint:
|
||||||
{ text: "bin_auto", hint: "Rounds values down to a fixed-size \'bin\', with control over the bin size and starting point provided by a query property." },
|
'Finds a row in the group that minimizes *ExprToMinimize*, and returns the value of *ExprToReturn* (or `*` to return the entire row).',
|
||||||
{ text: "binary_and", hint: "Returns a result of the bitwise `and` operation between two values." },
|
},
|
||||||
{ text: "binary_not", hint: "Returns a bitwise negation of the input value." },
|
{ text: 'array_concat', hint: 'Concatenates a number of dynamic arrays to a single array.' },
|
||||||
{ text: "binary_or", hint: "Returns a result of the bitwise `or` operation of the two values." },
|
{ text: 'array_length', hint: 'Calculates the number of elements in a dynamic array.' },
|
||||||
{ text: "binary_shift_left", hint: "Returns binary shift left operation on a pair of numbers." },
|
{ text: 'array_slice', hint: 'Extracts a slice of a dynamic array.' },
|
||||||
{ text: "binary_shift_right", hint: "Returns binary shift right operation on a pair of numbers." },
|
{
|
||||||
{ text: "binary_xor", hint: "Returns a result of the bitwise `xor` operation of the two values." },
|
text: 'array_split',
|
||||||
{ text: "buildschema", hint: "Returns the minimal schema that admits all values of *DynamicExpr*." },
|
hint:
|
||||||
{ text: "case", hint: "Evaluates a list of predicates and returns the first result expression whose predicate is satisfied." },
|
'Splits an array to multiple arrays according to the split indices and packs the generated array in a dynamic array.',
|
||||||
{ text: "ceiling", hint: "Calculates the smallest integer greater than, or equal to, the specified numeric expression." },
|
},
|
||||||
{ text: "cluster", hint: "Changes the reference of the query to a remote cluster." },
|
{
|
||||||
{ text: "coalesce", hint: "Evaluates a list of expressions and returns the first non-null (or non-empty for string) expression." },
|
text: 'asin',
|
||||||
{ text: "cos", hint: "Returns the cosine function." },
|
hint: 'Returns the angle whose sine is the specified number (the inverse operation of [`sin()`](sinfunction.md)) .',
|
||||||
{ text: "cot", hint: "Calculates the trigonometric cotangent of the specified angle, in radians." },
|
},
|
||||||
{ text: "count", hint: "Returns a count of the records per summarization group (or in total if summarization is done without grouping)." },
|
{
|
||||||
{ text: "countif", hint: "Returns a count of rows for which *Predicate* evaluates to `true`." },
|
text: 'assert',
|
||||||
{ text: "countof", hint: "Counts occurrences of a substring in a string. Plain string matches may overlap; regex matches do not." },
|
hint: 'Checks for a condition; if the condition is false, outputs error messages and fails the query.',
|
||||||
{ text: "current_principal", hint: "Returns the current principal running this query." },
|
},
|
||||||
{ text: "cursor_after", hint: "A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor." },
|
{
|
||||||
{ text: "cursor_before_or_at", hint: "A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor." },
|
text: 'atan',
|
||||||
{ text: "database", hint: "Changes the reference of the query to a specific database within the cluster scope." },
|
hint:
|
||||||
{ text: "datetime_add", hint: "Calculates a new [datetime](./scalar-data-types/datetime.md) from a specified datepart multiplied by a specified amount, added to a specified [datetime](./scalar-data-types/datetime.md)." },
|
'Returns the angle whose tangent is the specified number (the inverse operation of [`tan()`](tanfunction.md)) .',
|
||||||
{ text: "datetime_diff", hint: "Calculates calendarian difference between two [datetime](./scalar-data-types/datetime.md) values." },
|
},
|
||||||
{ text: "datetime_part", hint: "Extracts the requested date part as an integer value." },
|
{
|
||||||
{ text: "dayofmonth", hint: "Returns the integer number representing the day number of the given month" },
|
text: 'atan2',
|
||||||
{ text: "dayofweek", hint: "Returns the integer number of days since the preceding Sunday, as a `timespan`." },
|
hint:
|
||||||
{ text: "dayofyear", hint: "Returns the integer number represents the day number of the given year." },
|
'Calculates the angle, in radians, between the positive x-axis and the ray from the origin to the point (y, x).',
|
||||||
{ text: "dcount", hint: "Returns an estimate of the number of distinct values of *Expr* in the group." },
|
},
|
||||||
{ text: "dcount_hll", hint: "Calculates the dcount from hll results (which was generated by [hll](hll-aggfunction.md) or [hll_merge](hll-merge-aggfunction.md))." },
|
{ text: 'avg', hint: 'Calculates the average of *Expr* across the group.' },
|
||||||
{ text: "dcountif", hint: "Returns an estimate of the number of distinct values of *Expr* of rows for which *Predicate* evaluates to `true`." },
|
{
|
||||||
{ text: "degrees", hint: "Converts angle value in radians into value in degrees, using formula `degrees = (180 / PI ) * angle_in_radians`" },
|
text: 'avgif',
|
||||||
{ text: "distance", hint: "Returns the distance between two points in meters." },
|
hint:
|
||||||
{ text: "endofday", hint: "Returns the end of the day containing the date, shifted by an offset, if provided." },
|
'Calculates the [average](avg-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.',
|
||||||
{ text: "endofmonth", hint: "Returns the end of the month containing the date, shifted by an offset, if provided." },
|
},
|
||||||
{ text: "endofweek", hint: "Returns the end of the week containing the date, shifted by an offset, if provided." },
|
{ text: 'bag_keys', hint: 'Enumerates all the root keys in a dynamic property-bag object.' },
|
||||||
{ text: "endofyear", hint: "Returns the end of the year containing the date, shifted by an offset, if provided." },
|
{ text: 'base64_decodestring', hint: 'Decodes a base64 string to a UTF-8 string' },
|
||||||
{ text: "estimate_data_size", hint: "Returns an estimated data size of the selected columns of the tabular expression." },
|
{ text: 'base64_encodestring', hint: 'Encodes a string as base64 string' },
|
||||||
{ text: "exp", hint: "The base-e exponential function of x, which is e raised to the power x: e^x." },
|
{ text: 'beta_cdf', hint: 'Returns the standard cumulative beta distribution function.' },
|
||||||
{ text: "exp10", hint: "The base-10 exponential function of x, which is 10 raised to the power x: 10^x. \r\n**Syntax**" },
|
{ text: 'beta_inv', hint: 'Returns the inverse of the beta cumulative probability beta density function.' },
|
||||||
{ text: "exp2", hint: "The base-2 exponential function of x, which is 2 raised to the power x: 2^x." },
|
{ text: 'beta_pdf', hint: 'Returns the probability density beta function.' },
|
||||||
{ text: "extent_id", hint: "Returns a unique identifier that identifies the data shard (\"extent\") that the current record resides in." },
|
{ text: 'bin', hint: 'Rounds values down to an integer multiple of a given bin size.' },
|
||||||
{ text: "extent_tags", hint: "Returns a dynamic array with the [tags](../management/extents-overview.md#extent-tagging) of the data shard (\"extent\") that the current record resides in." },
|
{
|
||||||
{ text: "extract", hint: "Get a match for a [regular expression](./re2.md) from a text string." },
|
text: 'bin_at',
|
||||||
{ text: "extract_all", hint: "Get all matches for a [regular expression](./re2.md) from a text string." },
|
hint:
|
||||||
{ text: "extractjson", hint: "Get a specified element out of a JSON text using a path expression." },
|
"Rounds values down to a fixed-size 'bin', with control over the bin's starting point.\r\n(See also [`bin function`](./binfunction.md).)",
|
||||||
{ text: "floor", hint: "An alias for [`bin()`](binfunction.md)." },
|
},
|
||||||
{ text: "format_datetime", hint: "Formats a datetime parameter based on the format pattern parameter." },
|
{
|
||||||
{ text: "format_timespan", hint: "Formats a timespan parameter based on the format pattern parameter." },
|
text: 'bin_auto',
|
||||||
{ text: "gamma", hint: "Computes [gamma function](https://en.wikipedia.org/wiki/Gamma_function)" },
|
hint:
|
||||||
{ text: "getmonth", hint: "Get the month number (1-12) from a datetime." },
|
"Rounds values down to a fixed-size 'bin', with control over the bin size and starting point provided by a query property.",
|
||||||
{ text: "gettype", hint: "Returns the runtime type of its single argument." },
|
},
|
||||||
{ text: "getyear", hint: "Returns the year part of the `datetime` argument." },
|
{ text: 'binary_and', hint: 'Returns a result of the bitwise `and` operation between two values.' },
|
||||||
{ text: "hash", hint: "Returns a hash value for the input value." },
|
{ text: 'binary_not', hint: 'Returns a bitwise negation of the input value.' },
|
||||||
{ text: "hash_sha256", hint: "Returns a sha256 hash value for the input value." },
|
{ text: 'binary_or', hint: 'Returns a result of the bitwise `or` operation of the two values.' },
|
||||||
{ text: "hll", hint: "Calculates the Intermediate results of [dcount](dcount-aggfunction.md) across the group." },
|
{ text: 'binary_shift_left', hint: 'Returns binary shift left operation on a pair of numbers.' },
|
||||||
{ text: "hll_merge", hint: "Merges hll results (scalar version of the aggregate version [`hll_merge()`](hll-merge-aggfunction.md))." },
|
{ text: 'binary_shift_right', hint: 'Returns binary shift right operation on a pair of numbers.' },
|
||||||
{ text: "hourofday", hint: "Returns the integer number representing the hour number of the given date" },
|
{ text: 'binary_xor', hint: 'Returns a result of the bitwise `xor` operation of the two values.' },
|
||||||
{ text: "iff", hint: "Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third)." },
|
{ text: 'buildschema', hint: 'Returns the minimal schema that admits all values of *DynamicExpr*.' },
|
||||||
{ text: "iif", hint: "Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third)." },
|
{
|
||||||
{ text: "indexof", hint: "Function reports the zero-based index of the first occurrence of a specified string within input string." },
|
text: 'case',
|
||||||
{ text: "ingestion_time", hint: "Retrieves the record's `$IngestionTime` hidden `datetime` column, or null." },
|
hint: 'Evaluates a list of predicates and returns the first result expression whose predicate is satisfied.',
|
||||||
{ text: "iscolumnexists", hint: "Returns a boolean value indicating if the given string argument exists in the schema produced by the preceding tabular operator." },
|
},
|
||||||
{ text: "isempty", hint: "Returns `true` if the argument is an empty string or is null." },
|
{
|
||||||
{ text: "isfinite", hint: "Returns whether input is a finite value (is neither infinite nor NaN)." },
|
text: 'ceiling',
|
||||||
{ text: "isinf", hint: "Returns whether input is an infinite (positive or negative) value." },
|
hint: 'Calculates the smallest integer greater than, or equal to, the specified numeric expression.',
|
||||||
{ text: "isnan", hint: "Returns whether input is Not-a-Number (NaN) value." },
|
},
|
||||||
{ text: "isnotempty", hint: "Returns `true` if the argument is not an empty string nor it is a null." },
|
{ text: 'cluster', hint: 'Changes the reference of the query to a remote cluster.' },
|
||||||
{ text: "isnotnull", hint: "Returns `true` if the argument is not null." },
|
{
|
||||||
{ text: "isnull", hint: "Evaluates its sole argument and returns a `bool` value indicating if the argument evaluates to a null value." },
|
text: 'coalesce',
|
||||||
{ text: "log", hint: "Returns the natural logarithm function." },
|
hint: 'Evaluates a list of expressions and returns the first non-null (or non-empty for string) expression.',
|
||||||
{ text: "log10", hint: "Returns the common (base-10) logarithm function." },
|
},
|
||||||
{ text: "log2", hint: "Returns the base-2 logarithm function." },
|
{ text: 'cos', hint: 'Returns the cosine function.' },
|
||||||
{ text: "loggamma", hint: "Computes log of absolute value of the [gamma function](https://en.wikipedia.org/wiki/Gamma_function)" },
|
{ text: 'cot', hint: 'Calculates the trigonometric cotangent of the specified angle, in radians.' },
|
||||||
{ text: "make_datetime", hint: "Creates a [datetime](./scalar-data-types/datetime.md) scalar value from the specified date and time." },
|
{
|
||||||
{ text: "make_dictionary", hint: "Returns a `dynamic` (JSON) property-bag (dictionary) of all the values of *Expr* in the group." },
|
text: 'count',
|
||||||
{ text: "make_string", hint: "Returns the string generated by the Unicode characters." },
|
hint:
|
||||||
{ text: "make_timespan", hint: "Creates a [timespan](./scalar-data-types/timespan.md) scalar value from the specified time period." },
|
'Returns a count of the records per summarization group (or in total if summarization is done without grouping).',
|
||||||
{ text: "makelist", hint: "Returns a `dynamic` (JSON) array of all the values of *Expr* in the group." },
|
},
|
||||||
{ text: "makeset", hint: "Returns a `dynamic` (JSON) array of the set of distinct values that *Expr* takes in the group." },
|
{ text: 'countif', hint: 'Returns a count of rows for which *Predicate* evaluates to `true`.' },
|
||||||
{ text: "materialize", hint: "Allows caching a sub-query result during the time of query execution in a way that other subqueries can reference the partial result." },
|
{
|
||||||
{ text: "max", hint: "Returns the maximum value across the group." },
|
text: 'countof',
|
||||||
{ text: "max_of", hint: "Returns the maximum value of several evaluated numeric expressions." },
|
hint: 'Counts occurrences of a substring in a string. Plain string matches may overlap; regex matches do not.',
|
||||||
{ text: "merge_tdigests", hint: "Merges tdigest results (scalar version of the aggregate version [`merge_tdigests()`](merge-tdigests-aggfunction.md))." },
|
},
|
||||||
{ text: "min", hint: "Returns the minimum value agross the group." },
|
{ text: 'current_principal', hint: 'Returns the current principal running this query.' },
|
||||||
{ text: "min_of", hint: "Returns the minimum value of several evaluated numeric expressions." },
|
{
|
||||||
{ text: "monthofyear", hint: "Returns the integer number represents the month number of the given year." },
|
text: 'cursor_after',
|
||||||
{ text: "next", hint: "Returns the value of a column in a row that it at some offset following the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set)." },
|
hint: 'A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor.',
|
||||||
{ text: "not", hint: "Reverses the value of its `bool` argument." },
|
},
|
||||||
{ text: "now", hint: "Returns the current UTC clock time, optionally offset by a given timespan.\r\nThis function can be used multiple times in a statement and the clock time being referenced will be the same for all instances." },
|
{
|
||||||
{ text: "pack", hint: "Creates a `dynamic` object (property bag) from a list of names and values." },
|
text: 'cursor_before_or_at',
|
||||||
{ text: "pack_all", hint: "Creates a `dynamic` object (property bag) from all the columns of the tabular expression." },
|
hint: 'A predicate over the records of a table to compare their ingestion time\r\nagainst a database cursor.',
|
||||||
{ text: "pack_array", hint: "Packs all input values into a dynamic array." },
|
},
|
||||||
{ text: "parse_ipv4", hint: "Converts input to integer (signed 64-bit) number representation." },
|
{ text: 'database', hint: 'Changes the reference of the query to a specific database within the cluster scope.' },
|
||||||
{ text: "parse_json", hint: "Interprets a `string` as a [JSON value](https://json.org/)) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md). \r\nIt is superior to using [extractjson() function](./extractjsonfunction.md)\r\nwhen you need to extract more than one element of a JSON compound object." },
|
{
|
||||||
{ text: "parse_path", hint: "Parses a file path `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object that contains the following parts of the path: \r\nScheme, RootPath, DirectoryPath, DirectoryName, FileName, Extension, AlternateDataStreamName.\r\nIn addition to the simple paths with both types of slashes, supports paths with schemas (e.g. \"file://...\"), shared paths (e.g. \"\\\\shareddrive\\users...\"), long paths (e.g \"\\\\?\\C:...\"\"), alternate data streams (e.g. \"file1.exe:file2.exe\")" },
|
text: 'datetime_add',
|
||||||
{ text: "parse_url", hint: "Parses an absolute URL `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains all parts of the URL (Scheme, Host, Port, Path, Username, Password, Query Parameters, Fragment)." },
|
hint:
|
||||||
{ text: "parse_urlquery", hint: "Parses a url query `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains the Query parameters." },
|
'Calculates a new [datetime](./scalar-data-types/datetime.md) from a specified datepart multiplied by a specified amount, added to a specified [datetime](./scalar-data-types/datetime.md).',
|
||||||
{ text: "parse_user_agent", hint: "Interprets a user-agent string, which identifies the user's browser and provides certain system details to servers hosting the websites the user visits. The result is returned as [`dynamic`](./scalar-data-types/dynamic.md)." },
|
},
|
||||||
{ text: "parse_version", hint: "Converts input string representation of version to a comparable decimal number." },
|
{
|
||||||
{ text: "parse_xml", hint: "Interprets a `string` as a XML value, converts the value to a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md)." },
|
text: 'datetime_diff',
|
||||||
{ text: "percentile", hint: "Returns an estimate for the specified [nearest-rank percentile](#nearest-rank-percentile) of the population defined by *Expr*. \r\nThe accuracy depends on the density of population in the region of the percentile." },
|
hint: 'Calculates calendarian difference between two [datetime](./scalar-data-types/datetime.md) values.',
|
||||||
{ text: "percentile_tdigest", hint: "Calculates the percentile result from tdigest results (which was generated by [tdigest](tdigest-aggfunction.md) or [merge-tdigests](merge-tdigests-aggfunction.md))" },
|
},
|
||||||
{ text: "percentrank_tdigest", hint: "Calculates the approximate rank of the value in a set where rank is expressed as percentage of set's size. \r\nThis function can be viewed as the inverse of the percentile." },
|
{ text: 'datetime_part', hint: 'Extracts the requested date part as an integer value.' },
|
||||||
{ text: "pi", hint: "Returns the constant value of Pi (π)." },
|
{ text: 'dayofmonth', hint: 'Returns the integer number representing the day number of the given month' },
|
||||||
{ text: "point", hint: "Returns a dynamic array representation of a point." },
|
{ text: 'dayofweek', hint: 'Returns the integer number of days since the preceding Sunday, as a `timespan`.' },
|
||||||
{ text: "pow", hint: "Returns a result of raising to power" },
|
{ text: 'dayofyear', hint: 'Returns the integer number represents the day number of the given year.' },
|
||||||
{ text: "prev", hint: "Returns the value of a column in a row that it at some offset prior to the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set)." },
|
{ text: 'dcount', hint: 'Returns an estimate of the number of distinct values of *Expr* in the group.' },
|
||||||
{ text: "radians", hint: "Converts angle value in degrees into value in radians, using formula `radians = (PI / 180 ) * angle_in_degrees`" },
|
{
|
||||||
{ text: "rand", hint: "Returns a random number." },
|
text: 'dcount_hll',
|
||||||
{ text: "range", hint: "Generates a dynamic array holding a series of equally-spaced values." },
|
hint:
|
||||||
{ text: "repeat", hint: "Generates a dynamic array holding a series of equal values." },
|
'Calculates the dcount from hll results (which was generated by [hll](hll-aggfunction.md) or [hll_merge](hll-merge-aggfunction.md)).',
|
||||||
{ text: "replace", hint: "Replace all regex matches with another string." },
|
},
|
||||||
{ text: "reverse", hint: "Function makes reverse of input string." },
|
{
|
||||||
{ text: "round", hint: "Returns the rounded source to the specified precision." },
|
text: 'dcountif',
|
||||||
{ text: "row_cumsum", hint: "Calculates the cumulative sum of a column in a [serialized row set](./windowsfunctions.md#serialized-row-set)." },
|
hint:
|
||||||
{ text: "row_number", hint: "Returns the current row's index in a [serialized row set](./windowsfunctions.md#serialized-row-set).\r\nThe row index starts by default at `1` for the first row, and is incremented by `1` for each additional row.\r\nOptionally, the row index can start at a different value than `1`.\r\nAdditionally, the row index may be reset according to some provided predicate." },
|
'Returns an estimate of the number of distinct values of *Expr* of rows for which *Predicate* evaluates to `true`.',
|
||||||
{ text: "series_add", hint: "Calculates the element-wise addition of two numeric series inputs." },
|
},
|
||||||
{ text: "series_decompose", hint: "Applies a decomposition transformation on a series." },
|
{
|
||||||
{ text: "series_decompose_anomalies", hint: "Anomaly Detection based on series decomposition (refer to [series_decompose()](series-decomposefunction.md))" },
|
text: 'degrees',
|
||||||
{ text: "series_decompose_forecast", hint: "Forecast based on series decomposition." },
|
hint:
|
||||||
{ text: "series_divide", hint: "Calculates the element-wise division of two numeric series inputs." },
|
'Converts angle value in radians into value in degrees, using formula `degrees = (180 / PI ) * angle_in_radians`',
|
||||||
{ text: "series_equals", hint: "Calculates the element-wise equals (`==`) logic operation of two numeric series inputs." },
|
},
|
||||||
{ text: "series_fill_backward", hint: "Performs backward fill interpolation of missing values in a series." },
|
{ text: 'distance', hint: 'Returns the distance between two points in meters.' },
|
||||||
{ text: "series_fill_const", hint: "Replaces missing values in a series with a specified constant value." },
|
{ text: 'endofday', hint: 'Returns the end of the day containing the date, shifted by an offset, if provided.' },
|
||||||
{ text: "series_fill_forward", hint: "Performs forward fill interpolation of missing values in a series." },
|
{ text: 'endofmonth', hint: 'Returns the end of the month containing the date, shifted by an offset, if provided.' },
|
||||||
{ text: "series_fill_linear", hint: "Performs linear interpolation of missing values in a series." },
|
{ text: 'endofweek', hint: 'Returns the end of the week containing the date, shifted by an offset, if provided.' },
|
||||||
{ text: "series_fir", hint: "Applies a Finite Impulse Response filter on a series." },
|
{ text: 'endofyear', hint: 'Returns the end of the year containing the date, shifted by an offset, if provided.' },
|
||||||
{ text: "series_fit_2lines", hint: "Applies two segments linear regression on a series, returning multiple columns." },
|
{
|
||||||
{ text: "series_fit_2lines_dynamic", hint: "Applies two segments linear regression on a series, returning dynamic object." },
|
text: 'estimate_data_size',
|
||||||
{ text: "series_fit_line", hint: "Applies linear regression on a series, returning multiple columns." },
|
hint: 'Returns an estimated data size of the selected columns of the tabular expression.',
|
||||||
{ text: "series_fit_line_dynamic", hint: "Applies linear regression on a series, returning dynamic object." },
|
},
|
||||||
{ text: "series_greater", hint: "Calculates the element-wise greater (`>`) logic operation of two numeric series inputs." },
|
{ text: 'exp', hint: 'The base-e exponential function of x, which is e raised to the power x: e^x.' },
|
||||||
{ text: "series_greater_equals", hint: "Calculates the element-wise greater or equals (`>=`) logic operation of two numeric series inputs." },
|
{
|
||||||
{ text: "series_iir", hint: "Applies a Infinite Impulse Response filter on a series." },
|
text: 'exp10',
|
||||||
{ text: "series_less", hint: "Calculates the element-wise less (`<`) logic operation of two numeric series inputs." },
|
hint: 'The base-10 exponential function of x, which is 10 raised to the power x: 10^x. \r\n**Syntax**',
|
||||||
{ text: "series_less_equals", hint: "Calculates the element-wise less or equal (`<=`) logic operation of two numeric series inputs." },
|
},
|
||||||
{ text: "series_multiply", hint: "Calculates the element-wise multiplication of two numeric series inputs." },
|
{ text: 'exp2', hint: 'The base-2 exponential function of x, which is 2 raised to the power x: 2^x.' },
|
||||||
{ text: "series_not_equals", hint: "Calculates the element-wise not equals (`!=`) logic operation of two numeric series inputs." },
|
{
|
||||||
{ text: "series_outliers", hint: "Scores anomaly points in a series." },
|
text: 'extent_id',
|
||||||
{ text: "series_periods_detect", hint: "Finds the most significant periods that exist in a time series." },
|
hint: 'Returns a unique identifier that identifies the data shard ("extent") that the current record resides in.',
|
||||||
{ text: "series_periods_validate", hint: "Checks whether a time series contains periodic patterns of given lengths." },
|
},
|
||||||
{ text: "series_seasonal", hint: "Calculates the seasonal component of a series according to the detected or given seasonal period." },
|
{
|
||||||
{ text: "series_stats", hint: "Returns statistics for a series in multiple columns." },
|
text: 'extent_tags',
|
||||||
{ text: "series_stats_dynamic", hint: "Returns statistics for a series in dynamic object." },
|
hint:
|
||||||
{ text: "series_subtract", hint: "Calculates the element-wise subtraction of two numeric series inputs." },
|
'Returns a dynamic array with the [tags](../management/extents-overview.md#extent-tagging) of the data shard ("extent") that the current record resides in.',
|
||||||
{ text: "sign", hint: "Sign of a numeric expression" },
|
},
|
||||||
{ text: "sin", hint: "Returns the sine function." },
|
{ text: 'extract', hint: 'Get a match for a [regular expression](./re2.md) from a text string.' },
|
||||||
{ text: "split", hint: "Splits a given string according to a given delimiter and returns a string array with the contained substrings." },
|
{ text: 'extract_all', hint: 'Get all matches for a [regular expression](./re2.md) from a text string.' },
|
||||||
{ text: "sqrt", hint: "Returns the square root function." },
|
{ text: 'extractjson', hint: 'Get a specified element out of a JSON text using a path expression.' },
|
||||||
{ text: "startofday", hint: "Returns the start of the day containing the date, shifted by an offset, if provided." },
|
{ text: 'floor', hint: 'An alias for [`bin()`](binfunction.md).' },
|
||||||
{ text: "startofmonth", hint: "Returns the start of the month containing the date, shifted by an offset, if provided." },
|
{ text: 'format_datetime', hint: 'Formats a datetime parameter based on the format pattern parameter.' },
|
||||||
{ text: "startofweek", hint: "Returns the start of the week containing the date, shifted by an offset, if provided." },
|
{ text: 'format_timespan', hint: 'Formats a timespan parameter based on the format pattern parameter.' },
|
||||||
{ text: "startofyear", hint: "Returns the start of the year containing the date, shifted by an offset, if provided." },
|
{ text: 'gamma', hint: 'Computes [gamma function](https://en.wikipedia.org/wiki/Gamma_function)' },
|
||||||
{ text: "stdev", hint: "Calculates the standard deviation of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29)." },
|
{ text: 'getmonth', hint: 'Get the month number (1-12) from a datetime.' },
|
||||||
{ text: "stdevif", hint: "Calculates the [stdev](stdev-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`." },
|
{ text: 'gettype', hint: 'Returns the runtime type of its single argument.' },
|
||||||
{ text: "stdevp", hint: "Calculates the standard deviation of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population)." },
|
{ text: 'getyear', hint: 'Returns the year part of the `datetime` argument.' },
|
||||||
{ text: "strcat", hint: "Concatenates between 1 and 64 arguments." },
|
{ text: 'hash', hint: 'Returns a hash value for the input value.' },
|
||||||
{ text: "strcat_array", hint: "Creates a concatenated string of array values using specified delimiter." },
|
{ text: 'hash_sha256', hint: 'Returns a sha256 hash value for the input value.' },
|
||||||
{ text: "strcat_delim", hint: "Concatenates between 2 and 64 arguments, with delimiter, provided as first argument." },
|
{ text: 'hll', hint: 'Calculates the Intermediate results of [dcount](dcount-aggfunction.md) across the group.' },
|
||||||
{ text: "strcmp", hint: "Compares two strings." },
|
{
|
||||||
{ text: "string_size", hint: "Returns the size, in bytes, of the input string." },
|
text: 'hll_merge',
|
||||||
{ text: "strlen", hint: "Returns the length, in characters, of the input string." },
|
hint: 'Merges hll results (scalar version of the aggregate version [`hll_merge()`](hll-merge-aggfunction.md)).',
|
||||||
{ text: "strrep", hint: "Repeats given [string](./scalar-data-types/string.md) provided amount of times." },
|
},
|
||||||
{ text: "substring", hint: "Extracts a substring from a source string starting from some index to the end of the string." },
|
{ text: 'hourofday', hint: 'Returns the integer number representing the hour number of the given date' },
|
||||||
{ text: "sum", hint: "Calculates the sum of *Expr* across the group." },
|
{
|
||||||
{ text: "sumif", hint: "Returns a sum of *Expr* for which *Predicate* evaluates to `true`." },
|
text: 'iff',
|
||||||
{ text: "table", hint: "References specific table using an query-time evaluated string-expression." },
|
hint:
|
||||||
{ text: "tan", hint: "Returns the tangent function." },
|
'Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third).',
|
||||||
{ text: "tdigest", hint: "Calculates the Intermediate results of [`percentiles()`](percentiles-aggfunction.md) across the group." },
|
},
|
||||||
{ text: "tdigest_merge", hint: "Merges tdigest results (scalar version of the aggregate version [`tdigest_merge()`](tdigest-merge-aggfunction.md))." },
|
{
|
||||||
{ text: "tobool", hint: "Converts input to boolean (signed 8-bit) representation." },
|
text: 'iif',
|
||||||
{ text: "todatetime", hint: "Converts input to [datetime](./scalar-data-types/datetime.md) scalar." },
|
hint:
|
||||||
{ text: "todecimal", hint: "Converts input to decimal number representation." },
|
'Evaluates the first argument (the predicate), and returns the value of either the second or third arguments, depending on whether the predicate evaluated to `true` (second) or `false` (third).',
|
||||||
{ text: "todouble", hint: "Converts the input to a value of type `real`. (`todouble()` and `toreal()` are synonyms.)" },
|
},
|
||||||
{ text: "todynamic", hint: "Interprets a `string` as a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md)." },
|
{
|
||||||
{ text: "toguid", hint: "Converts input to [`guid`](./scalar-data-types/guid.md) representation." },
|
text: 'indexof',
|
||||||
{ text: "tohex", hint: "Converts input to a hexadecimal string." },
|
hint: 'Function reports the zero-based index of the first occurrence of a specified string within input string.',
|
||||||
{ text: "toint", hint: "Converts input to integer (signed 32-bit) number representation." },
|
},
|
||||||
{ text: "tolong", hint: "Converts input to long (signed 64-bit) number representation." },
|
{ text: 'ingestion_time', hint: "Retrieves the record's `$IngestionTime` hidden `datetime` column, or null." },
|
||||||
{ text: "tolower", hint: "Converts input string to lower case." },
|
{
|
||||||
{ text: "toscalar", hint: "Returns a scalar constant value of the evaluated expression." },
|
text: 'iscolumnexists',
|
||||||
{ text: "tostring", hint: "Converts input to a string representation." },
|
hint:
|
||||||
{ text: "totimespan", hint: "Converts input to [timespan](./scalar-data-types/timespan.md) scalar." },
|
'Returns a boolean value indicating if the given string argument exists in the schema produced by the preceding tabular operator.',
|
||||||
{ text: "toupper", hint: "Converts a string to upper case." },
|
},
|
||||||
{ text: "translate", hint: "Replaces a set of characters ('searchList') with another set of characters ('replacementList') in a given a string.\r\nThe function searches for characters in the 'searchList' and replaces them with the corresponding characters in 'replacementList'" },
|
{ text: 'isempty', hint: 'Returns `true` if the argument is an empty string or is null.' },
|
||||||
{ text: "treepath", hint: "Enumerates all the path expressions that identify leaves in a dynamic object." },
|
{ text: 'isfinite', hint: 'Returns whether input is a finite value (is neither infinite nor NaN).' },
|
||||||
{ text: "trim", hint: "Removes all leading and trailing matches of the specified regular expression." },
|
{ text: 'isinf', hint: 'Returns whether input is an infinite (positive or negative) value.' },
|
||||||
{ text: "trim_end", hint: "Removes trailing match of the specified regular expression." },
|
{ text: 'isnan', hint: 'Returns whether input is Not-a-Number (NaN) value.' },
|
||||||
{ text: "trim_start", hint: "Removes leading match of the specified regular expression." },
|
{ text: 'isnotempty', hint: 'Returns `true` if the argument is not an empty string nor it is a null.' },
|
||||||
{ text: "url_decode", hint: "The function converts encoded URL into a to regular URL representation." },
|
{ text: 'isnotnull', hint: 'Returns `true` if the argument is not null.' },
|
||||||
{ text: "url_encode", hint: "The function converts characters of the input URL into a format that can be transmitted over the Internet." },
|
{
|
||||||
{ text: "variance", hint: "Calculates the variance of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29)." },
|
text: 'isnull',
|
||||||
{ text: "varianceif", hint: "Calculates the [variance](variance-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`." },
|
hint:
|
||||||
{ text: "variancep", hint: "Calculates the variance of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population)." },
|
'Evaluates its sole argument and returns a `bool` value indicating if the argument evaluates to a null value.',
|
||||||
{ text: "weekofyear", hint: "Returns the integer number represents the week number." },
|
},
|
||||||
{ text: "welch_test", hint: "Computes the p_value of the [Welch-test function](https://en.wikipedia.org/wiki/Welch%27s_t-test)" },
|
{ text: 'log', hint: 'Returns the natural logarithm function.' },
|
||||||
{ text: "zip", hint: "The `zip` function accepts any number of `dynamic` arrays, and returns an\r\narray whose elements are each an array holding the elements of the input\r\narrays of the same index." },
|
{ text: 'log10', hint: 'Returns the common (base-10) logarithm function.' },
|
||||||
|
{ text: 'log2', hint: 'Returns the base-2 logarithm function.' },
|
||||||
|
{
|
||||||
|
text: 'loggamma',
|
||||||
|
hint: 'Computes log of absolute value of the [gamma function](https://en.wikipedia.org/wiki/Gamma_function)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'make_datetime',
|
||||||
|
hint: 'Creates a [datetime](./scalar-data-types/datetime.md) scalar value from the specified date and time.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'make_dictionary',
|
||||||
|
hint: 'Returns a `dynamic` (JSON) property-bag (dictionary) of all the values of *Expr* in the group.',
|
||||||
|
},
|
||||||
|
{ text: 'make_string', hint: 'Returns the string generated by the Unicode characters.' },
|
||||||
|
{
|
||||||
|
text: 'make_timespan',
|
||||||
|
hint: 'Creates a [timespan](./scalar-data-types/timespan.md) scalar value from the specified time period.',
|
||||||
|
},
|
||||||
|
{ text: 'makelist', hint: 'Returns a `dynamic` (JSON) array of all the values of *Expr* in the group.' },
|
||||||
|
{
|
||||||
|
text: 'makeset',
|
||||||
|
hint: 'Returns a `dynamic` (JSON) array of the set of distinct values that *Expr* takes in the group.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'materialize',
|
||||||
|
hint:
|
||||||
|
'Allows caching a sub-query result during the time of query execution in a way that other subqueries can reference the partial result.',
|
||||||
|
},
|
||||||
|
{ text: 'max', hint: 'Returns the maximum value across the group.' },
|
||||||
|
{ text: 'max_of', hint: 'Returns the maximum value of several evaluated numeric expressions.' },
|
||||||
|
{
|
||||||
|
text: 'merge_tdigests',
|
||||||
|
hint:
|
||||||
|
'Merges tdigest results (scalar version of the aggregate version [`merge_tdigests()`](merge-tdigests-aggfunction.md)).',
|
||||||
|
},
|
||||||
|
{ text: 'min', hint: 'Returns the minimum value agross the group.' },
|
||||||
|
{ text: 'min_of', hint: 'Returns the minimum value of several evaluated numeric expressions.' },
|
||||||
|
{ text: 'monthofyear', hint: 'Returns the integer number represents the month number of the given year.' },
|
||||||
|
{
|
||||||
|
text: 'next',
|
||||||
|
hint:
|
||||||
|
'Returns the value of a column in a row that it at some offset following the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set).',
|
||||||
|
},
|
||||||
|
{ text: 'not', hint: 'Reverses the value of its `bool` argument.' },
|
||||||
|
{
|
||||||
|
text: 'now',
|
||||||
|
hint:
|
||||||
|
'Returns the current UTC clock time, optionally offset by a given timespan.\r\nThis function can be used multiple times in a statement and the clock time being referenced will be the same for all instances.',
|
||||||
|
},
|
||||||
|
{ text: 'pack', hint: 'Creates a `dynamic` object (property bag) from a list of names and values.' },
|
||||||
|
{
|
||||||
|
text: 'pack_all',
|
||||||
|
hint: 'Creates a `dynamic` object (property bag) from all the columns of the tabular expression.',
|
||||||
|
},
|
||||||
|
{ text: 'pack_array', hint: 'Packs all input values into a dynamic array.' },
|
||||||
|
{ text: 'parse_ipv4', hint: 'Converts input to integer (signed 64-bit) number representation.' },
|
||||||
|
{
|
||||||
|
text: 'parse_json',
|
||||||
|
hint:
|
||||||
|
'Interprets a `string` as a [JSON value](https://json.org/)) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md). \r\nIt is superior to using [extractjson() function](./extractjsonfunction.md)\r\nwhen you need to extract more than one element of a JSON compound object.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'parse_path',
|
||||||
|
hint:
|
||||||
|
'Parses a file path `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object that contains the following parts of the path: \r\nScheme, RootPath, DirectoryPath, DirectoryName, FileName, Extension, AlternateDataStreamName.\r\nIn addition to the simple paths with both types of slashes, supports paths with schemas (e.g. "file://..."), shared paths (e.g. "\\\\shareddrive\\users..."), long paths (e.g "\\\\?\\C:...""), alternate data streams (e.g. "file1.exe:file2.exe")',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'parse_url',
|
||||||
|
hint:
|
||||||
|
'Parses an absolute URL `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains all parts of the URL (Scheme, Host, Port, Path, Username, Password, Query Parameters, Fragment).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'parse_urlquery',
|
||||||
|
hint:
|
||||||
|
'Parses a url query `string` and returns a [`dynamic`](./scalar-data-types/dynamic.md) object contains the Query parameters.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'parse_user_agent',
|
||||||
|
hint:
|
||||||
|
"Interprets a user-agent string, which identifies the user's browser and provides certain system details to servers hosting the websites the user visits. The result is returned as [`dynamic`](./scalar-data-types/dynamic.md).",
|
||||||
|
},
|
||||||
|
{ text: 'parse_version', hint: 'Converts input string representation of version to a comparable decimal number.' },
|
||||||
|
{
|
||||||
|
text: 'parse_xml',
|
||||||
|
hint:
|
||||||
|
'Interprets a `string` as a XML value, converts the value to a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'percentile',
|
||||||
|
hint:
|
||||||
|
'Returns an estimate for the specified [nearest-rank percentile](#nearest-rank-percentile) of the population defined by *Expr*. \r\nThe accuracy depends on the density of population in the region of the percentile.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'percentile_tdigest',
|
||||||
|
hint:
|
||||||
|
'Calculates the percentile result from tdigest results (which was generated by [tdigest](tdigest-aggfunction.md) or [merge-tdigests](merge-tdigests-aggfunction.md))',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'percentrank_tdigest',
|
||||||
|
hint:
|
||||||
|
"Calculates the approximate rank of the value in a set where rank is expressed as percentage of set's size. \r\nThis function can be viewed as the inverse of the percentile.",
|
||||||
|
},
|
||||||
|
{ text: 'pi', hint: 'Returns the constant value of Pi (π).' },
|
||||||
|
{ text: 'point', hint: 'Returns a dynamic array representation of a point.' },
|
||||||
|
{ text: 'pow', hint: 'Returns a result of raising to power' },
|
||||||
|
{
|
||||||
|
text: 'prev',
|
||||||
|
hint:
|
||||||
|
'Returns the value of a column in a row that it at some offset prior to the\r\ncurrent row in a [serialized row set](./windowsfunctions.md#serialized-row-set).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'radians',
|
||||||
|
hint:
|
||||||
|
'Converts angle value in degrees into value in radians, using formula `radians = (PI / 180 ) * angle_in_degrees`',
|
||||||
|
},
|
||||||
|
{ text: 'rand', hint: 'Returns a random number.' },
|
||||||
|
{ text: 'range', hint: 'Generates a dynamic array holding a series of equally-spaced values.' },
|
||||||
|
{ text: 'repeat', hint: 'Generates a dynamic array holding a series of equal values.' },
|
||||||
|
{ text: 'replace', hint: 'Replace all regex matches with another string.' },
|
||||||
|
{ text: 'reverse', hint: 'Function makes reverse of input string.' },
|
||||||
|
{ text: 'round', hint: 'Returns the rounded source to the specified precision.' },
|
||||||
|
{
|
||||||
|
text: 'row_cumsum',
|
||||||
|
hint:
|
||||||
|
'Calculates the cumulative sum of a column in a [serialized row set](./windowsfunctions.md#serialized-row-set).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'row_number',
|
||||||
|
hint:
|
||||||
|
"Returns the current row's index in a [serialized row set](./windowsfunctions.md#serialized-row-set).\r\nThe row index starts by default at `1` for the first row, and is incremented by `1` for each additional row.\r\nOptionally, the row index can start at a different value than `1`.\r\nAdditionally, the row index may be reset according to some provided predicate.",
|
||||||
|
},
|
||||||
|
{ text: 'series_add', hint: 'Calculates the element-wise addition of two numeric series inputs.' },
|
||||||
|
{ text: 'series_decompose', hint: 'Applies a decomposition transformation on a series.' },
|
||||||
|
{
|
||||||
|
text: 'series_decompose_anomalies',
|
||||||
|
hint:
|
||||||
|
'Anomaly Detection based on series decomposition (refer to [series_decompose()](series-decomposefunction.md))',
|
||||||
|
},
|
||||||
|
{ text: 'series_decompose_forecast', hint: 'Forecast based on series decomposition.' },
|
||||||
|
{ text: 'series_divide', hint: 'Calculates the element-wise division of two numeric series inputs.' },
|
||||||
|
{
|
||||||
|
text: 'series_equals',
|
||||||
|
hint: 'Calculates the element-wise equals (`==`) logic operation of two numeric series inputs.',
|
||||||
|
},
|
||||||
|
{ text: 'series_fill_backward', hint: 'Performs backward fill interpolation of missing values in a series.' },
|
||||||
|
{ text: 'series_fill_const', hint: 'Replaces missing values in a series with a specified constant value.' },
|
||||||
|
{ text: 'series_fill_forward', hint: 'Performs forward fill interpolation of missing values in a series.' },
|
||||||
|
{ text: 'series_fill_linear', hint: 'Performs linear interpolation of missing values in a series.' },
|
||||||
|
{ text: 'series_fir', hint: 'Applies a Finite Impulse Response filter on a series.' },
|
||||||
|
{
|
||||||
|
text: 'series_fit_2lines',
|
||||||
|
hint: 'Applies two segments linear regression on a series, returning multiple columns.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'series_fit_2lines_dynamic',
|
||||||
|
hint: 'Applies two segments linear regression on a series, returning dynamic object.',
|
||||||
|
},
|
||||||
|
{ text: 'series_fit_line', hint: 'Applies linear regression on a series, returning multiple columns.' },
|
||||||
|
{ text: 'series_fit_line_dynamic', hint: 'Applies linear regression on a series, returning dynamic object.' },
|
||||||
|
{
|
||||||
|
text: 'series_greater',
|
||||||
|
hint: 'Calculates the element-wise greater (`>`) logic operation of two numeric series inputs.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'series_greater_equals',
|
||||||
|
hint: 'Calculates the element-wise greater or equals (`>=`) logic operation of two numeric series inputs.',
|
||||||
|
},
|
||||||
|
{ text: 'series_iir', hint: 'Applies a Infinite Impulse Response filter on a series.' },
|
||||||
|
{ text: 'series_less', hint: 'Calculates the element-wise less (`<`) logic operation of two numeric series inputs.' },
|
||||||
|
{
|
||||||
|
text: 'series_less_equals',
|
||||||
|
hint: 'Calculates the element-wise less or equal (`<=`) logic operation of two numeric series inputs.',
|
||||||
|
},
|
||||||
|
{ text: 'series_multiply', hint: 'Calculates the element-wise multiplication of two numeric series inputs.' },
|
||||||
|
{
|
||||||
|
text: 'series_not_equals',
|
||||||
|
hint: 'Calculates the element-wise not equals (`!=`) logic operation of two numeric series inputs.',
|
||||||
|
},
|
||||||
|
{ text: 'series_outliers', hint: 'Scores anomaly points in a series.' },
|
||||||
|
{ text: 'series_periods_detect', hint: 'Finds the most significant periods that exist in a time series.' },
|
||||||
|
{
|
||||||
|
text: 'series_periods_validate',
|
||||||
|
hint: 'Checks whether a time series contains periodic patterns of given lengths.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'series_seasonal',
|
||||||
|
hint: 'Calculates the seasonal component of a series according to the detected or given seasonal period.',
|
||||||
|
},
|
||||||
|
{ text: 'series_stats', hint: 'Returns statistics for a series in multiple columns.' },
|
||||||
|
{ text: 'series_stats_dynamic', hint: 'Returns statistics for a series in dynamic object.' },
|
||||||
|
{ text: 'series_subtract', hint: 'Calculates the element-wise subtraction of two numeric series inputs.' },
|
||||||
|
{ text: 'sign', hint: 'Sign of a numeric expression' },
|
||||||
|
{ text: 'sin', hint: 'Returns the sine function.' },
|
||||||
|
{
|
||||||
|
text: 'split',
|
||||||
|
hint:
|
||||||
|
'Splits a given string according to a given delimiter and returns a string array with the contained substrings.',
|
||||||
|
},
|
||||||
|
{ text: 'sqrt', hint: 'Returns the square root function.' },
|
||||||
|
{ text: 'startofday', hint: 'Returns the start of the day containing the date, shifted by an offset, if provided.' },
|
||||||
|
{
|
||||||
|
text: 'startofmonth',
|
||||||
|
hint: 'Returns the start of the month containing the date, shifted by an offset, if provided.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'startofweek',
|
||||||
|
hint: 'Returns the start of the week containing the date, shifted by an offset, if provided.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'startofyear',
|
||||||
|
hint: 'Returns the start of the year containing the date, shifted by an offset, if provided.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'stdev',
|
||||||
|
hint:
|
||||||
|
'Calculates the standard deviation of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'stdevif',
|
||||||
|
hint:
|
||||||
|
'Calculates the [stdev](stdev-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'stdevp',
|
||||||
|
hint:
|
||||||
|
'Calculates the standard deviation of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population).',
|
||||||
|
},
|
||||||
|
{ text: 'strcat', hint: 'Concatenates between 1 and 64 arguments.' },
|
||||||
|
{ text: 'strcat_array', hint: 'Creates a concatenated string of array values using specified delimiter.' },
|
||||||
|
{
|
||||||
|
text: 'strcat_delim',
|
||||||
|
hint: 'Concatenates between 2 and 64 arguments, with delimiter, provided as first argument.',
|
||||||
|
},
|
||||||
|
{ text: 'strcmp', hint: 'Compares two strings.' },
|
||||||
|
{ text: 'string_size', hint: 'Returns the size, in bytes, of the input string.' },
|
||||||
|
{ text: 'strlen', hint: 'Returns the length, in characters, of the input string.' },
|
||||||
|
{ text: 'strrep', hint: 'Repeats given [string](./scalar-data-types/string.md) provided amount of times.' },
|
||||||
|
{
|
||||||
|
text: 'substring',
|
||||||
|
hint: 'Extracts a substring from a source string starting from some index to the end of the string.',
|
||||||
|
},
|
||||||
|
{ text: 'sum', hint: 'Calculates the sum of *Expr* across the group.' },
|
||||||
|
{ text: 'sumif', hint: 'Returns a sum of *Expr* for which *Predicate* evaluates to `true`.' },
|
||||||
|
{ text: 'table', hint: 'References specific table using an query-time evaluated string-expression.' },
|
||||||
|
{ text: 'tan', hint: 'Returns the tangent function.' },
|
||||||
|
{
|
||||||
|
text: 'tdigest',
|
||||||
|
hint: 'Calculates the Intermediate results of [`percentiles()`](percentiles-aggfunction.md) across the group.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'tdigest_merge',
|
||||||
|
hint:
|
||||||
|
'Merges tdigest results (scalar version of the aggregate version [`tdigest_merge()`](tdigest-merge-aggfunction.md)).',
|
||||||
|
},
|
||||||
|
{ text: 'tobool', hint: 'Converts input to boolean (signed 8-bit) representation.' },
|
||||||
|
{ text: 'todatetime', hint: 'Converts input to [datetime](./scalar-data-types/datetime.md) scalar.' },
|
||||||
|
{ text: 'todecimal', hint: 'Converts input to decimal number representation.' },
|
||||||
|
{
|
||||||
|
text: 'todouble',
|
||||||
|
hint: 'Converts the input to a value of type `real`. (`todouble()` and `toreal()` are synonyms.)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'todynamic',
|
||||||
|
hint:
|
||||||
|
'Interprets a `string` as a [JSON value](https://json.org/) and returns the value as [`dynamic`](./scalar-data-types/dynamic.md).',
|
||||||
|
},
|
||||||
|
{ text: 'toguid', hint: 'Converts input to [`guid`](./scalar-data-types/guid.md) representation.' },
|
||||||
|
{ text: 'tohex', hint: 'Converts input to a hexadecimal string.' },
|
||||||
|
{ text: 'toint', hint: 'Converts input to integer (signed 32-bit) number representation.' },
|
||||||
|
{ text: 'tolong', hint: 'Converts input to long (signed 64-bit) number representation.' },
|
||||||
|
{ text: 'tolower', hint: 'Converts input string to lower case.' },
|
||||||
|
{ text: 'toscalar', hint: 'Returns a scalar constant value of the evaluated expression.' },
|
||||||
|
{ text: 'tostring', hint: 'Converts input to a string representation.' },
|
||||||
|
{ text: 'totimespan', hint: 'Converts input to [timespan](./scalar-data-types/timespan.md) scalar.' },
|
||||||
|
{ text: 'toupper', hint: 'Converts a string to upper case.' },
|
||||||
|
{
|
||||||
|
text: 'translate',
|
||||||
|
hint:
|
||||||
|
"Replaces a set of characters ('searchList') with another set of characters ('replacementList') in a given a string.\r\nThe function searches for characters in the 'searchList' and replaces them with the corresponding characters in 'replacementList'",
|
||||||
|
},
|
||||||
|
{ text: 'treepath', hint: 'Enumerates all the path expressions that identify leaves in a dynamic object.' },
|
||||||
|
{ text: 'trim', hint: 'Removes all leading and trailing matches of the specified regular expression.' },
|
||||||
|
{ text: 'trim_end', hint: 'Removes trailing match of the specified regular expression.' },
|
||||||
|
{ text: 'trim_start', hint: 'Removes leading match of the specified regular expression.' },
|
||||||
|
{ text: 'url_decode', hint: 'The function converts encoded URL into a to regular URL representation.' },
|
||||||
|
{
|
||||||
|
text: 'url_encode',
|
||||||
|
hint: 'The function converts characters of the input URL into a format that can be transmitted over the Internet.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'variance',
|
||||||
|
hint:
|
||||||
|
'Calculates the variance of *Expr* across the group, considering the group as a [sample](https://en.wikipedia.org/wiki/Sample_%28statistics%29).',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'varianceif',
|
||||||
|
hint:
|
||||||
|
'Calculates the [variance](variance-aggfunction.md) of *Expr* across the group for which *Predicate* evaluates to `true`.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'variancep',
|
||||||
|
hint:
|
||||||
|
'Calculates the variance of *Expr* across the group, considering the group as a [population](https://en.wikipedia.org/wiki/Statistical_population).',
|
||||||
|
},
|
||||||
|
{ text: 'weekofyear', hint: 'Returns the integer number represents the week number.' },
|
||||||
|
{
|
||||||
|
text: 'welch_test',
|
||||||
|
hint: 'Computes the p_value of the [Welch-test function](https://en.wikipedia.org/wiki/Welch%27s_t-test)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'zip',
|
||||||
|
hint:
|
||||||
|
'The `zip` function accepts any number of `dynamic` arrays, and returns an\r\narray whose elements are each an array holding the elements of the input\r\narrays of the same index.',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const KEYWORDS = [
|
export const KEYWORDS = [
|
||||||
@ -297,8 +644,16 @@ export const KEYWORDS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const grafanaMacros = [
|
export const grafanaMacros = [
|
||||||
{ text: '$__timeFilter', display: '$__timeFilter()', hint: 'Macro that uses the selected timerange in Grafana to filter the query.', },
|
{
|
||||||
{ text: '$__escapeMulti', display: '$__escapeMulti()', hint: 'Macro to escape multi-value template variables that contain illegal characters.', },
|
text: '$__timeFilter',
|
||||||
|
display: '$__timeFilter()',
|
||||||
|
hint: 'Macro that uses the selected timerange in Grafana to filter the query.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '$__escapeMulti',
|
||||||
|
display: '$__escapeMulti()',
|
||||||
|
hint: 'Macro to escape multi-value template variables that contain illegal characters.',
|
||||||
|
},
|
||||||
{ text: '$__contains', display: '$__contains()', hint: 'Macro for multi-value template variables.' },
|
{ text: '$__contains', display: '$__contains()', hint: 'Macro for multi-value template variables.' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(this.handleQueryCtrlError.bind(this));
|
.catch(this.handleQueryCtrlError.bind(this));
|
||||||
}
|
};
|
||||||
|
|
||||||
getAzureLogAnalyticsSchema = () => {
|
getAzureLogAnalyticsSchema = () => {
|
||||||
return this.getWorkspaces()
|
return this.getWorkspaces()
|
||||||
@ -322,15 +322,15 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace);
|
return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace);
|
||||||
})
|
})
|
||||||
.catch(this.handleQueryCtrlError.bind(this));
|
.catch(this.handleQueryCtrlError.bind(this));
|
||||||
}
|
};
|
||||||
|
|
||||||
onLogAnalyticsQueryChange = (nextQuery: string) => {
|
onLogAnalyticsQueryChange = (nextQuery: string) => {
|
||||||
this.target.azureLogAnalytics.query = nextQuery;
|
this.target.azureLogAnalytics.query = nextQuery;
|
||||||
}
|
};
|
||||||
|
|
||||||
onLogAnalyticsQueryExecute = () => {
|
onLogAnalyticsQueryExecute = () => {
|
||||||
this.panelCtrl.refresh();
|
this.panelCtrl.refresh();
|
||||||
}
|
};
|
||||||
|
|
||||||
get templateVariables() {
|
get templateVariables() {
|
||||||
return this.templateSrv.variables.map(t => '$' + t.name);
|
return this.templateSrv.variables.map(t => '$' + t.name);
|
||||||
@ -380,16 +380,15 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
|||||||
|
|
||||||
onAppInsightsQueryChange = (nextQuery: string) => {
|
onAppInsightsQueryChange = (nextQuery: string) => {
|
||||||
this.target.appInsights.rawQueryString = nextQuery;
|
this.target.appInsights.rawQueryString = nextQuery;
|
||||||
}
|
};
|
||||||
|
|
||||||
onAppInsightsQueryExecute = () => {
|
onAppInsightsQueryExecute = () => {
|
||||||
return this.refresh();
|
return this.refresh();
|
||||||
}
|
};
|
||||||
|
|
||||||
getAppInsightsQuerySchema = () => {
|
getAppInsightsQuerySchema = () => {
|
||||||
return this.datasource.appInsightsDatasource.getQuerySchema()
|
return this.datasource.appInsightsDatasource.getQuerySchema().catch(this.handleQueryCtrlError.bind(this));
|
||||||
.catch(this.handleQueryCtrlError.bind(this));
|
};
|
||||||
}
|
|
||||||
|
|
||||||
getAppInsightsGroupBySegments(query) {
|
getAppInsightsGroupBySegments(query) {
|
||||||
return _.map(this.target.appInsights.groupByOptions, option => {
|
return _.map(this.target.appInsights.groupByOptions, option => {
|
||||||
|
@ -58,7 +58,9 @@ export class LokiQueryEditor extends PureComponent<Props> {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<div className="gf-form-label">Loki is currently not supported as dashboard data source. We are working on it!</div>
|
<div className="gf-form-label">
|
||||||
|
Loki is currently not supported as dashboard data source. We are working on it!
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/*
|
{/*
|
||||||
<LokiQueryField
|
<LokiQueryField
|
||||||
|
@ -93,7 +93,7 @@ export class LokiDatasource {
|
|||||||
|
|
||||||
// add search term to stream & add to array
|
// add search term to stream & add to array
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
for (const stream of (result.data.streams || [])) {
|
for (const stream of result.data.streams || []) {
|
||||||
stream.search = query.regexp;
|
stream.search = query.regexp;
|
||||||
allStreams.push(stream);
|
allStreams.push(stream);
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,3 @@ export interface LokiQuery extends DataQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type LokiQueryResultFormats = 'time_series' | 'logs';
|
export type LokiQueryResultFormats = 'time_series' | 'logs';
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ describe('MSSQLDatasource', () => {
|
|||||||
};
|
};
|
||||||
const time = {
|
const time = {
|
||||||
from: moment(1521545610656),
|
from: moment(1521545610656),
|
||||||
to: moment(1521546251185)
|
to: moment(1521546251185),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -3,4 +3,3 @@ import { DataQuery } from '@grafana/ui/src/types';
|
|||||||
export interface PromQuery extends DataQuery {
|
export interface PromQuery extends DataQuery {
|
||||||
expr: string;
|
expr: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ export class QueryEditor extends PureComponent<Props> {
|
|||||||
...this.props.query,
|
...this.props.query,
|
||||||
scenarioId: item.value,
|
scenarioId: item.value,
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { query } = this.props;
|
const { query } = this.props;
|
||||||
|
@ -98,4 +98,3 @@ export class TestDataDatasource implements DataSourceApi<TestDataQuery> {
|
|||||||
return this.backendSrv.get('/api/tsdb/testdata/scenarios');
|
return this.backendSrv.get('/api/tsdb/testdata/scenarios');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,4 +8,3 @@ export interface Scenario {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ class LegendTableHeaderItem extends PureComponent<LegendTableHeaderProps & Legen
|
|||||||
export class Legend extends PureComponent<GraphLegendProps> {
|
export class Legend extends PureComponent<GraphLegendProps> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<CustomScrollbar renderTrackHorizontal={(props) => <div {...props} style={{visibility: 'none'}} />}>
|
<CustomScrollbar renderTrackHorizontal={props => <div {...props} style={{ visibility: 'none' }} />}>
|
||||||
<GraphLegend {...this.props} />
|
<GraphLegend {...this.props} />
|
||||||
</CustomScrollbar>
|
</CustomScrollbar>
|
||||||
);
|
);
|
||||||
|
@ -46,7 +46,6 @@ class GraphElement {
|
|||||||
legendElem: HTMLElement;
|
legendElem: HTMLElement;
|
||||||
|
|
||||||
constructor(private scope, private elem, private timeSrv) {
|
constructor(private scope, private elem, private timeSrv) {
|
||||||
|
|
||||||
this.ctrl = scope.ctrl;
|
this.ctrl = scope.ctrl;
|
||||||
this.dashboard = this.ctrl.dashboard;
|
this.dashboard = this.ctrl.dashboard;
|
||||||
this.panel = this.ctrl.panel;
|
this.panel = this.ctrl.panel;
|
||||||
|
@ -37,7 +37,7 @@ export class TimeRegionFormCtrl {
|
|||||||
line: false,
|
line: false,
|
||||||
// Default colors for new
|
// Default colors for new
|
||||||
fillColor: 'rgba(234, 112, 112, 0.12)',
|
fillColor: 'rgba(234, 112, 112, 0.12)',
|
||||||
lineColor: 'rgba(237, 46, 24, 0.60)'
|
lineColor: 'rgba(237, 46, 24, 0.60)',
|
||||||
});
|
});
|
||||||
this.panelCtrl.render();
|
this.panelCtrl.render();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelOptionsProps, Switch } from '@grafana/ui';
|
import { PanelOptionsProps, Switch } from '@grafana/ui';
|
||||||
import { Options } from './types';
|
import { Options } from './types';
|
||||||
|
@ -250,7 +250,10 @@ function drawSimpleOpacityLegend(elem, options) {
|
|||||||
.attr('stroke-width', 0)
|
.attr('stroke-width', 0)
|
||||||
.attr(
|
.attr(
|
||||||
'fill',
|
'fill',
|
||||||
getColorFromHexRgbOrName(options.cardColor, contextSrv.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark)
|
getColorFromHexRgbOrName(
|
||||||
|
options.cardColor,
|
||||||
|
contextSrv.user.lightTheme ? GrafanaThemeType.Light : GrafanaThemeType.Dark
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.style('opacity', d => legendOpacityScale(d));
|
.style('opacity', d => legendOpacityScale(d));
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user