Compare commits

...

12 Commits

Author SHA1 Message Date
Alex Khomenko
0b109a1637 Forgot password: Fix styling (#26002)
(cherry picked from commit 9e47114c45)
2020-07-02 13:07:33 +03:00
Arve Knudsen
6a1f05d7ec CircleCI: Upgrade build pipeline tool (#26006)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 3e9e2db384)
2020-07-02 13:07:33 +03:00
Ryan McKinley
1576b16219 Monaco: check suggestions against current word (#25992)
* trigger on current word

* proper index

* test suggestsions

* test suggestsions

* fix test

(cherry picked from commit 085b2f3dbf)
2020-07-02 13:07:33 +03:00
Sofia Papagiannaki
44ba5482f1 Release v7.1.0-beta2 2020-07-02 13:07:33 +03:00
Ryan McKinley
972e07bd2e Panel Loading: spin clockwise, not counter clockwise (#25998)
* spin clockwise

* spin clockwise

(cherry picked from commit 90a5a85eb1)
2020-07-02 13:07:33 +03:00
Sebastian Widmer
22211e5bdd Loki: Allow aliasing Loki queries in dashboard (#25706)
* Loki: Add Legend field to query editor

* Loki: Basic test for legend field

* Loki: Mention legend is only for metric queries

* Loki: Fix absolute timerange never updating

(cherry picked from commit 5789f80e14)
2020-07-02 13:07:33 +03:00
Ryan McKinley
d95c494d9d Value Mappings: remove unused operator property from interface (#25989)
(cherry picked from commit 73e82af4df)
2020-07-02 13:07:33 +03:00
Ivana Huckova
6e3a9d7927 Fix href to datasources for NoDataSourceCallToAction in Explore (#25991)
(cherry picked from commit c9751707c5)
2020-07-02 13:07:33 +03:00
Dan Cech
4a68ba7b23 provide license token directly via plugin environment (#25987)
* provide license token directly via plugin environment

(cherry picked from commit b5ca2381bc)
2020-07-02 13:07:33 +03:00
Sofia Papagiannaki
74ca7121eb Fix build-in plugins failing to load in windows (#25982)
(cherry picked from commit bcaa42fbb3)
2020-07-02 13:07:33 +03:00
Marcus Andersson
4d1ea72426 release 7.1.0-beta1 2020-07-01 12:13:45 +02:00
Marcus Andersson
11385c6cfe bumped version to beta1. 2020-07-01 12:11:50 +02:00
33 changed files with 446 additions and 139 deletions

View File

@@ -56,7 +56,7 @@ commands:
- run:
name: "Install Grafana build pipeline tool"
command: |
VERSION=0.4.17
VERSION=0.4.18
curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
chmod +x grabpl
mv grabpl /tmp

View File

@@ -2,5 +2,5 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
"version": "7.1.0-pre.0"
"version": "7.1.0-beta.2"
}

View File

@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
"version": "7.1.0-pre",
"version": "7.1.0-beta2",
"repository": "github:grafana/grafana",
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/data",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana Data Library",
"keywords": [
"typescript"

View File

@@ -152,8 +152,8 @@ describe('Format value', () => {
it('should return formatted value if there are no matching value mappings', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
{ id: 0, text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 1, text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
];
const value = '10';
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
@@ -186,8 +186,8 @@ describe('Format value', () => {
it('should return mapped value if there are matching value mappings', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
];
const value = '11';
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
@@ -196,9 +196,7 @@ describe('Format value', () => {
});
it('should return mapped value and leave numeric value in tact if value mapping maps to empty string', () => {
const valueMappings: ValueMapping[] = [
{ id: 1, operator: '', text: '', type: MappingType.ValueToText, value: '1' },
];
const valueMappings: ValueMapping[] = [{ id: 1, text: '', type: MappingType.ValueToText, value: '1' }];
const value = '1';
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });

View File

@@ -4,9 +4,8 @@ export enum MappingType {
}
interface BaseMap {
id: number;
operator: string;
text: string;
id: number; // this could/should just be the array index
text: string; // the final display value
type: MappingType;
}

View File

@@ -11,8 +11,8 @@ describe('Format value with value mappings', () => {
it('should return undefined with no matching valuemappings', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 1, operator: '', text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
{ id: 0, text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 1, text: '1-9', type: MappingType.RangeToText, from: '1', to: '9' },
];
const value = '10';
@@ -21,8 +21,8 @@ describe('Format value with value mappings', () => {
it('should return first matching mapping with lowest id', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, operator: '', text: 'tio', type: MappingType.ValueToText, value: '10' },
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, text: 'tio', type: MappingType.ValueToText, value: '10' },
];
const value = '10';
@@ -31,8 +31,8 @@ describe('Format value with value mappings', () => {
it('should return if value is null and value to text mapping value is null', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, operator: '', text: '<NULL>', type: MappingType.ValueToText, value: 'null' },
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, text: '<NULL>', type: MappingType.ValueToText, value: 'null' },
];
const value = null;
@@ -41,8 +41,8 @@ describe('Format value with value mappings', () => {
it('should return if value is null and range to text mapping from and to is null', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '<NULL>', type: MappingType.RangeToText, from: 'null', to: 'null' },
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 0, text: '<NULL>', type: MappingType.RangeToText, from: 'null', to: 'null' },
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
];
const value = null;
@@ -51,8 +51,8 @@ describe('Format value with value mappings', () => {
it('should return rangeToText mapping where value equals to', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-10', type: MappingType.RangeToText, from: '1', to: '10' },
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 0, text: '1-10', type: MappingType.RangeToText, from: '1', to: '10' },
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
];
const value = '10';
@@ -61,8 +61,8 @@ describe('Format value with value mappings', () => {
it('should return rangeToText mapping where value equals from', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '10-20', type: MappingType.RangeToText, from: '10', to: '20' },
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 0, text: '10-20', type: MappingType.RangeToText, from: '10', to: '20' },
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
];
const value = '10';
@@ -71,8 +71,8 @@ describe('Format value with value mappings', () => {
it('should return rangeToText mapping where value is between from and to', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, operator: '', text: 'elva', type: MappingType.ValueToText, value: '11' },
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, text: 'elva', type: MappingType.ValueToText, value: '11' },
];
const value = '10';
@@ -81,8 +81,8 @@ describe('Format value with value mappings', () => {
it('should map value text to mapping', () => {
const valueMappings: ValueMapping[] = [
{ id: 0, operator: '', text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, operator: '', text: 'ELVA', type: MappingType.ValueToText, value: 'elva' },
{ id: 0, text: '1-20', type: MappingType.RangeToText, from: '1', to: '20' },
{ id: 1, text: 'ELVA', type: MappingType.ValueToText, value: 'elva' },
];
const value = 'elva';

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e-selectors",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana End-to-End Test Selectors Library",
"keywords": [
"cli",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana End-to-End Test Library",
"keywords": [
"cli",
@@ -45,7 +45,7 @@
"types": "src/index.ts",
"dependencies": {
"@cypress/webpack-preprocessor": "4.1.3",
"@grafana/e2e-selectors": "7.1.0-pre.0",
"@grafana/e2e-selectors": "7.1.0-beta.2",
"@grafana/tsconfig": "^1.0.0-rc1",
"@mochajs/json-file-reporter": "^1.2.0",
"blink-diff": "1.0.13",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/runtime",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana Runtime Library",
"keywords": [
"grafana",
@@ -23,8 +23,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@grafana/data": "7.1.0-pre.0",
"@grafana/ui": "7.1.0-pre.0",
"@grafana/data": "7.1.0-beta.2",
"@grafana/ui": "7.1.0-beta.2",
"systemjs": "0.20.19",
"systemjs-plugin-css": "0.1.37"
},
@@ -32,9 +32,9 @@
"@grafana/tsconfig": "^1.0.0-rc1",
"@rollup/plugin-commonjs": "11.0.2",
"@rollup/plugin-node-resolve": "7.1.1",
"@types/jest": "23.3.14",
"@types/rollup-plugin-visualizer": "2.6.0",
"@types/systemjs": "^0.20.6",
"@types/jest": "23.3.14",
"lodash": "4.17.15",
"pretty-format": "25.1.0",
"rollup": "2.0.6",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/toolkit",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana Toolkit",
"keywords": [
"grafana",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/ui",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -28,8 +28,8 @@
},
"dependencies": {
"@emotion/core": "^10.0.27",
"@grafana/data": "7.1.0-pre.0",
"@grafana/e2e-selectors": "7.1.0-pre.0",
"@grafana/data": "7.1.0-beta.2",
"@grafana/e2e-selectors": "7.1.0-beta.2",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "^1.0.0",
@@ -47,9 +47,8 @@
"immutable": "3.8.2",
"jquery": "3.5.1",
"lodash": "4.17.15",
"monaco-editor": "0.20.0",
"react-monaco-editor": "0.36.0",
"moment": "2.24.0",
"monaco-editor": "0.20.0",
"papaparse": "4.6.3",
"rc-cascader": "1.0.1",
"rc-drawer": "3.1.3",
@@ -63,6 +62,7 @@
"react-dom": "16.12.0",
"react-highlight-words": "0.16.0",
"react-hook-form": "5.1.3",
"react-monaco-editor": "0.36.0",
"react-popper": "1.3.3",
"react-storybook-addon-props-combinations": "1.1.0",
"react-table": "7.0.0",

View File

@@ -0,0 +1,38 @@
import { findInsertIndex } from './suggestions';
describe('Check suggestion index', () => {
it('find last $ sign', () => {
const line = ' hello $123';
const { index, prefix } = findInsertIndex(line);
expect(index).toEqual(line.indexOf('$'));
expect(prefix).toEqual('$123');
});
it('insert into empty line', () => {
const line = '';
const { index, prefix } = findInsertIndex(line);
expect(index).toEqual(0);
expect(prefix).toEqual('');
});
it('insert new word', () => {
const line = 'this is a new ';
const { index, prefix } = findInsertIndex(line);
expect(index).toEqual(line.length);
expect(prefix).toEqual('');
});
it('complte a simple word', () => {
const line = 'SELECT * FROM tab';
const { index, prefix } = findInsertIndex(line);
expect(index).toEqual(line.lastIndexOf(' ') + 1);
expect(prefix).toEqual('tab');
});
it('complete a quoted word', () => {
const line = 'SELECT "hello", "wo';
const { index, prefix } = findInsertIndex(line);
expect(index).toEqual(line.lastIndexOf('"') + 1);
expect(prefix).toEqual('wo');
});
});

View File

@@ -2,6 +2,33 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { CodeEditorSuggestionItem, CodeEditorSuggestionItemKind, CodeEditorSuggestionProvider } from './types';
/**
* @internal -- only exported for tests
*/
export function findInsertIndex(line: string): { index: number; prefix: string } {
for (let i = line.length - 1; i > 0; i--) {
const ch = line.charAt(i);
if (ch === '$') {
return {
index: i,
prefix: line.substring(i),
};
}
// Keep these seperators
if (ch === ' ' || ch === '\t' || ch === '"' || ch === "'") {
return {
index: i + 1,
prefix: line.substring(i + 1),
};
}
}
return {
index: 0,
prefix: line,
};
}
function getCompletionItems(
prefix: string,
suggestions: CodeEditorSuggestionItem[],
@@ -53,51 +80,39 @@ export function registerSuggestions(
triggerCharacters: ['$'],
provideCompletionItems: (model, position, context) => {
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: position.column,
endColumn: position.column,
};
// Simple check if this was triggered by pressing `$`
if (context.triggerCharacter === '$') {
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: position.column - 1,
endColumn: position.column,
};
range.startColumn = position.column - 1;
return {
suggestions: getCompletionItems('$', getSuggestions(), range),
};
}
// find out if we are completing a property in the 'dependencies' object.
const lineText = model.getValueInRange({
// Find the replacement region
const currentLine = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column,
});
const idx = lineText.lastIndexOf('$');
if (idx >= 0) {
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: idx, // the last $ we found
endColumn: position.column,
};
return {
suggestions: getCompletionItems(lineText.substr(idx), getSuggestions(), range),
};
const { index, prefix } = findInsertIndex(currentLine);
range.startColumn = index + 1;
const suggestions = getCompletionItems(prefix, getSuggestions(), range);
if (suggestions.length) {
// NOTE, this will replace any language provided suggestions
return { suggestions };
}
// Empty line that asked for suggestion
if (lineText.trim().length < 1) {
return {
suggestions: getCompletionItems('', getSuggestions(), {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: position.column,
endColumn: position.column,
}),
};
}
// console.log('complete?', lineText, context);
// Default language suggestions
return undefined;
},
});

View File

@@ -12,8 +12,8 @@ const setup = (spy?: any, propOverrides?: object) => {
}
},
valueMappings: [
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' },
{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
],
};
@@ -35,9 +35,7 @@ describe('On remove mapping', () => {
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
remove.at(0).simulate('click');
expect(onChangeSpy).toBeCalledWith([
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
]);
expect(onChangeSpy).toBeCalledWith([{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' }]);
});
it('should remove mapping at index 1', () => {
@@ -47,9 +45,7 @@ describe('On remove mapping', () => {
const remove = wrapper.find('button[aria-label="ValueMappingsEditor remove button"]');
remove.at(1).simulate('click');
expect(onChangeSpy).toBeCalledWith([
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
]);
expect(onChangeSpy).toBeCalledWith([{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' }]);
});
});
@@ -62,9 +58,9 @@ describe('Next id to add', () => {
add.at(0).simulate('click');
expect(onChangeSpy).toBeCalledWith([
{ id: 1, operator: '', type: MappingType.ValueToText, value: '20', text: 'Ok' },
{ id: 2, operator: '', type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
{ id: 3, operator: '', type: MappingType.ValueToText, from: '', to: '', text: '' },
{ id: 1, type: MappingType.ValueToText, value: '20', text: 'Ok' },
{ id: 2, type: MappingType.RangeToText, from: '21', to: '30', text: 'Meh' },
{ id: 3, type: MappingType.ValueToText, from: '', to: '', text: '' },
]);
});
@@ -73,8 +69,6 @@ describe('Next id to add', () => {
const wrapper = setup(onChangeSpy, { valueMappings: [] });
const add = wrapper.find('*[aria-label="ValueMappingsEditor add mapping button"]');
add.at(0).simulate('click');
expect(onChangeSpy).toBeCalledWith([
{ id: 0, operator: '', type: MappingType.ValueToText, from: '', to: '', text: '' },
]);
expect(onChangeSpy).toBeCalledWith([{ id: 0, type: MappingType.ValueToText, from: '', to: '', text: '' }]);
});
});

View File

@@ -15,7 +15,6 @@ export const ValueMappingsEditor: React.FC<Props> = ({ valueMappings, onChange,
type: MappingType.ValueToText,
from: '',
to: '',
operator: '',
text: '',
};
const id = update && update.length > 0 ? Math.max(...update.map(v => v.id)) + 1 : 0;

View File

@@ -155,6 +155,7 @@ export const getStandardFieldConfigs = () => {
id: 'mappings',
path: 'mappings',
name: 'Value mappings',
description: 'Modify the display text based on input value',
editor: standardEditorsRegistry.get('mappings').editor as any,
override: standardEditorsRegistry.get('mappings').editor as any,

View File

@@ -1,6 +1,6 @@
{
"name": "@jaegertracing/jaeger-ui-components",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -14,7 +14,7 @@
"typescript": "3.9.3"
},
"dependencies": {
"@grafana/data": "7.1.0-pre.0",
"@grafana/data": "7.1.0-beta.2",
"@types/classnames": "^2.2.7",
"@types/deep-freeze": "^0.1.1",
"@types/hoist-non-react-statics": "^3.3.1",

View File

@@ -16,4 +16,6 @@ type Licensing interface {
LicenseURL(user *SignedInUser) string
StateInfo() string
TokenRaw() string
}

View File

@@ -100,7 +100,11 @@ func (m *manager) Register(pluginID string, factory PluginFactoryFunc) error {
}
if m.License.HasLicense() {
hostEnv = append(hostEnv, fmt.Sprintf("GF_ENTERPRISE_LICENSE_PATH=%s", m.Cfg.EnterpriseLicensePath))
hostEnv = append(
hostEnv,
fmt.Sprintf("GF_ENTERPRISE_LICENSE_PATH=%s", m.Cfg.EnterpriseLicensePath),
fmt.Sprintf("GF_ENTERPRISE_LICENSE_TEXT=%s", m.License.TokenRaw()),
)
}
env := pluginSettings.ToEnv("GF_PLUGIN", hostEnv)

View File

@@ -251,6 +251,7 @@ func TestManager(t *testing.T) {
t.Run("Plugin registration scenario when Grafana is licensed", func(t *testing.T) {
ctx.license.edition = "Enterprise"
ctx.license.hasLicense = true
ctx.license.tokenRaw = "testtoken"
ctx.cfg.BuildVersion = "7.0.0"
ctx.cfg.EnterpriseLicensePath = "/license.txt"
@@ -258,8 +259,8 @@ func TestManager(t *testing.T) {
require.NoError(t, err)
t.Run("Should provide expected host environment variables", func(t *testing.T) {
require.Len(t, ctx.env, 3)
require.EqualValues(t, []string{"GF_VERSION=7.0.0", "GF_EDITION=Enterprise", "GF_ENTERPRISE_LICENSE_PATH=/license.txt"}, ctx.env)
require.Len(t, ctx.env, 4)
require.EqualValues(t, []string{"GF_VERSION=7.0.0", "GF_EDITION=Enterprise", "GF_ENTERPRISE_LICENSE_PATH=/license.txt", "GF_ENTERPRISE_LICENSE_TEXT=testtoken"}, ctx.env)
})
})
})
@@ -383,6 +384,7 @@ func (tp *testPlugin) CallResource(ctx context.Context, req *backend.CallResourc
type testLicensingService struct {
edition string
hasLicense bool
tokenRaw string
}
func (t *testLicensingService) HasLicense() bool {
@@ -408,3 +410,7 @@ func (t *testLicensingService) LicenseURL(user *models.SignedInUser) string {
func (t *testLicensingService) HasValidLicense() bool {
return false
}
func (t *testLicensingService) TokenRaw() string {
return t.tokenRaw
}

View File

@@ -3,6 +3,7 @@ package plugins
import (
"net/url"
"path"
"path/filepath"
"strings"
"github.com/grafana/grafana/pkg/setting"
@@ -63,7 +64,9 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
// Previously there was an assumption that the plugin directory
// should be public/app/plugins/<plugin type>/<plugin id>
// However this can be an issue if the plugin directory should be renamed to something else
currentDir := path.Base(fp.PluginDir)
currentDir := filepath.Base(fp.PluginDir)
// use path package for the following statements
// because these are not file paths
fp.Module = path.Join("app/plugins", fp.Type, currentDir, "module")
fp.BaseUrl = path.Join("public/app/plugins", fp.Type, currentDir)
}

View File

@@ -56,3 +56,7 @@ func (l *OSSLicensingService) Init() error {
func (*OSSLicensingService) HasValidLicense() bool {
return false
}
func (*OSSLicensingService) TokenRaw() string {
return ""
}

View File

@@ -1,6 +1,6 @@
{
"name": "@grafana-plugins/input-datasource",
"version": "7.1.0-pre.0",
"version": "7.1.0-beta.2",
"description": "Input Datasource",
"private": true,
"repository": {
@@ -16,9 +16,9 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"devDependencies": {
"@grafana/data": "7.1.0-pre.0",
"@grafana/toolkit": "7.1.0-pre.0",
"@grafana/ui": "7.1.0-pre.0"
"@grafana/data": "7.1.0-beta.2",
"@grafana/toolkit": "7.1.0-beta.2",
"@grafana/ui": "7.1.0-beta.2"
},
"volta": {
"node": "12.16.2"

View File

@@ -45,7 +45,7 @@ export const ForgottenPassword: FC = () => {
<Legend>Reset password</Legend>
<Field
label="User"
description="Enter your informaton to get a reset link sent to you"
description="Enter your information to get a reset link sent to you"
invalid={!!errors.userOrEmail}
error={errors?.userOrEmail?.message}
>

View File

@@ -49,10 +49,11 @@ export const getLoginStyles = (theme: GrafanaTheme) => {
min-height: 100vh;
background-position: center;
background-repeat: no-repeat;
background-color: ${theme.palette.black};
min-width: 100%;
margin-left: 0;
background-color: $black;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`,
@@ -76,7 +77,7 @@ export const getLoginStyles = (theme: GrafanaTheme) => {
text-align: center;
`,
mainTitle: css`
font-size: '32px';
font-size: 32px;
`,
subTitle: css`
font-size: ${theme.typography.size.md};

View File

@@ -95,7 +95,7 @@ export class PanelHeader extends Component<Props, State> {
return (
<div className="panel-loading" onClick={this.onCancelQuery}>
<Tooltip content="Cancel query">
<Icon className="panel-loading__spinner spin-counter-clock" name="sync" />
<Icon className="panel-loading__spinner spin-clockwise" name="sync" />
</Tooltip>
</div>
);

View File

@@ -23,7 +23,7 @@ export const NoDataSourceCallToAction = () => {
);
const ctaElement = (
<LinkButton size="lg" href="/datasources/new" icon="database">
<LinkButton size="lg" href="datasources/new" icon="database">
Add data source
</LinkButton>
);

View File

@@ -0,0 +1,66 @@
import React from 'react';
import { shallow } from 'enzyme';
import { toUtc } from '@grafana/data';
import { LokiQueryEditor } from './LokiQueryEditor';
import { LokiDatasource } from '../datasource';
import { LokiQuery } from '../types';
const createMockRequestRange = (from: string, to: string) => {
return {
request: {
range: {
from: toUtc(from, 'YYYY-MM-DD'),
to: toUtc(to, 'YYYY-MM-DD'),
},
},
};
};
const setup = (propOverrides?: object) => {
const datasourceMock: unknown = {};
const datasource: LokiDatasource = datasourceMock as LokiDatasource;
const onRunQuery = jest.fn();
const onChange = jest.fn();
const query: LokiQuery = {
expr: '',
refId: 'A',
legendFormat: 'My Legend',
};
const data = createMockRequestRange('2020-01-01', '2020-01-02');
const props: any = {
datasource,
onChange,
onRunQuery,
query,
data,
};
Object.assign(props, propOverrides);
const wrapper = shallow(<LokiQueryEditor {...props} />);
const instance = wrapper.instance() as LokiQueryEditor;
return {
instance,
wrapper,
};
};
describe('Render LokiQueryEditor with legend', () => {
it('should render', () => {
const { wrapper } = setup();
expect(wrapper).toMatchSnapshot();
});
it('should update absolute timerange', () => {
const { wrapper } = setup();
wrapper.setProps({
data: createMockRequestRange('2019-01-01', '2020-01-02'),
});
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -1,45 +1,105 @@
// Libraries
import React, { memo } from 'react';
import React, { PureComponent } from 'react';
// Types
import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data';
import { AbsoluteTimeRange, QueryEditorProps, PanelData } from '@grafana/data';
import { InlineFormLabel } from '@grafana/ui';
import { LokiDatasource } from '../datasource';
import { LokiQuery } from '../types';
import { LokiQueryField } from './LokiQueryField';
type Props = QueryEditorProps<LokiDatasource, LokiQuery>;
export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) {
const { query, data, datasource, onChange, onRunQuery } = props;
interface State {
legendFormat: string;
}
let absolute: AbsoluteTimeRange;
export class LokiQueryEditor extends PureComponent<Props, State> {
// Query target to be modified and used for queries
query: LokiQuery;
if (data && data.request) {
const { range } = data.request;
absolute = {
from: range.from.valueOf(),
to: range.to.valueOf(),
};
} else {
absolute = {
from: Date.now() - 10000,
to: Date.now(),
constructor(props: Props) {
super(props);
// Use default query to prevent undefined input values
const defaultQuery: Partial<LokiQuery> = { expr: '', legendFormat: '' };
const query = Object.assign({}, defaultQuery, props.query);
this.query = query;
// Query target properties that are fully controlled inputs
this.state = {
// Fully controlled text inputs
legendFormat: query.legendFormat,
};
}
return (
<div>
<LokiQueryField
datasource={datasource}
query={query}
onChange={onChange}
onRunQuery={onRunQuery}
history={[]}
data={data}
absoluteRange={absolute}
/>
</div>
);
});
calcAbsoluteRange = (data: PanelData): AbsoluteTimeRange => {
if (data && data.request) {
const { range } = data.request;
return {
from: range.from.valueOf(),
to: range.to.valueOf(),
};
}
return {
from: Date.now() - 10000,
to: Date.now(),
};
};
onFieldChange = (query: LokiQuery, override?: any) => {
this.query.expr = query.expr;
};
onLegendChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
const legendFormat = e.currentTarget.value;
this.query.legendFormat = legendFormat;
this.setState({ legendFormat });
};
onRunQuery = () => {
const { query } = this;
this.props.onChange(query);
this.props.onRunQuery();
};
render() {
const { datasource, query, data } = this.props;
const { legendFormat } = this.state;
return (
<div>
<LokiQueryField
datasource={datasource}
query={query}
onChange={this.onFieldChange}
onRunQuery={this.onRunQuery}
history={[]}
data={data}
absoluteRange={this.calcAbsoluteRange(data)}
/>
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
width={7}
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
>
Legend
</InlineFormLabel>
<input
type="text"
className="gf-form-input"
placeholder="legend format"
value={legendFormat}
onChange={this.onLegendChange}
onBlur={this.onRunQuery}
/>
</div>
</div>
</div>
);
}
}
export default LokiQueryEditor;

View File

@@ -0,0 +1,115 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render LokiQueryEditor with legend should render 1`] = `
<div>
<Component
absoluteRange={
Object {
"from": 1577836800000,
"to": 1577923200000,
}
}
data={
Object {
"request": Object {
"range": Object {
"from": "2020-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
},
},
}
}
datasource={Object {}}
history={Array []}
onChange={[Function]}
onRunQuery={[Function]}
query={
Object {
"expr": "",
"legendFormat": "My Legend",
"refId": "A",
}
}
/>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<Component
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={7}
>
Legend
</Component>
<input
className="gf-form-input"
onBlur={[Function]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
</div>
</div>
</div>
`;
exports[`Render LokiQueryEditor with legend should update absolute timerange 1`] = `
<div>
<Component
absoluteRange={
Object {
"from": 1546300800000,
"to": 1577923200000,
}
}
data={
Object {
"request": Object {
"range": Object {
"from": "2019-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
},
},
}
}
datasource={Object {}}
history={Array []}
onChange={[Function]}
onRunQuery={[Function]}
query={
Object {
"expr": "",
"legendFormat": "My Legend",
"refId": "A",
}
}
/>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<Component
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={7}
>
Legend
</Component>
<input
className="gf-form-input"
onBlur={[Function]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
</div>
</div>
</div>
`;

View File

@@ -143,8 +143,10 @@ function createUid(ts: string, labelsString: string, line: string): string {
}
function lokiMatrixToTimeSeries(matrixResult: LokiMatrixResult, options: TransformerOptions): TimeSeries {
const name = createMetricLabel(matrixResult.metric, options);
return {
target: createMetricLabel(matrixResult.metric, options),
target: name,
title: name,
datapoints: lokiPointsToTimeseriesPoints(matrixResult.values, options),
tags: matrixResult.metric,
meta: options.meta,

View File

@@ -35,15 +35,15 @@
}
}
@keyframes spin-counter-clock {
@keyframes spin-clockwise {
0% {
transform: rotate(359deg);
transform: rotate(0deg) scaleX(-1); // scaleX flips the `sync` icon so arrows point the correct way
}
100% {
transform: rotate(0deg);
transform: rotate(359deg) scaleX(-1);
}
}
.spin-counter-clock {
animation: spin-counter-clock 3s infinite linear;
.spin-clockwise {
animation: spin-clockwise 3s infinite linear;
}