mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Webpack (#9391)
* webpack poc, this is not going to work for plugins, dam * tech: webpack and systemjs for plugins starting to work * tech: webpack and systemjs combo starting to work * tech: webpack + karma tests progress * tech: webpack + karma progress * tech: working on tests * tech: webpack * tech: webpack + karma, all tests pass * tech: webpack + karma, all tests pass * tech: webpack all tests pass * webpack: getting closer * tech: webpack progress * webpack: further build refinements * webpack: ng annotate fixes * webpack: optimized build fix * tech: minor fix for elasticsearch * tech: webpack + ace editor * tech: restored lodash move mixin compatability * tech: added enzyme react test and upgraded to react v16 * tech: package version fix * tech: added testdata to built in bundle * webpack: sass progress * tech: prod & dev build is working for the sass * tech: clean up unused grunt stuff and moved to scripts folder * tech: added vendor and manifest chunks, updated readme and docs * tech: webpack finishing touches
This commit is contained in:
parent
f6cc741a6d
commit
0c86241c5b
@ -1,7 +1,7 @@
|
||||
[run]
|
||||
init_cmds = [
|
||||
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
|
||||
["./bin/grafana-server"]
|
||||
["./bin/grafana-server", "cfg:app_mode=development"]
|
||||
]
|
||||
watch_all = true
|
||||
watch_dirs = [
|
||||
@ -9,9 +9,9 @@ watch_dirs = [
|
||||
"$WORKDIR/public/views",
|
||||
"$WORKDIR/conf",
|
||||
]
|
||||
watch_exts = [".go", ".ini", ".toml", ".html"]
|
||||
watch_exts = [".go", ".ini", ".toml"]
|
||||
build_delay = 1500
|
||||
cmds = [
|
||||
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
|
||||
["./bin/grafana-server"]
|
||||
["./bin/grafana-server", "cfg:app_mode=development"]
|
||||
]
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,6 +4,8 @@ coverage/
|
||||
.aws-config.json
|
||||
awsconfig
|
||||
/dist
|
||||
/public/build
|
||||
/public/views/index.html
|
||||
/emails/dist
|
||||
/public_gen
|
||||
/public/vendor/npm
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"browser": true,
|
||||
|
||||
"esversion": 6,
|
||||
"bitwise":false,
|
||||
"curly": true,
|
||||
"eqnull": true,
|
||||
|
@ -31,7 +31,7 @@ module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
// load task definitions
|
||||
grunt.loadTasks('tasks');
|
||||
grunt.loadTasks('./scripts/grunt');
|
||||
|
||||
// Utility function to load plugin settings into config
|
||||
function loadConfig(config,path) {
|
||||
@ -46,7 +46,7 @@ module.exports = function (grunt) {
|
||||
}
|
||||
|
||||
// Merge that object with what with whatever we have here
|
||||
loadConfig(config,'./tasks/options/');
|
||||
loadConfig(config,'./scripts/grunt/options/');
|
||||
// pass the config to grunt
|
||||
grunt.initConfig(config);
|
||||
};
|
||||
|
15
README.md
15
README.md
@ -46,10 +46,21 @@ yarn install --pure-lockfile
|
||||
npm run build
|
||||
```
|
||||
|
||||
To build the frontend assets only on changes:
|
||||
To rebuild frontend assets (typesript, sass etc) as you change them start
|
||||
the watcher via.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
npm run watch
|
||||
```
|
||||
|
||||
Run tests
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
Run tests in watch mode
|
||||
```bash
|
||||
npm run test-watch
|
||||
```
|
||||
|
||||
### Recompile backend on source change
|
||||
|
@ -27,7 +27,7 @@ go get github.com/grafana/grafana
|
||||
|
||||
On Windows use setx instead of export and then restart your command prompt:
|
||||
```
|
||||
setx GOPATH %cd%
|
||||
setx GOPATH %cd%
|
||||
```
|
||||
|
||||
You may see an error such as: `package github.com/grafana/grafana: no buildable Go source files`. This is just a warning, and you can proceed with the directions.
|
||||
@ -43,7 +43,7 @@ go run build.go build # (or 'go build ./pkg/cmd/grafana-server')
|
||||
The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on windows you need
|
||||
to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download).
|
||||
|
||||
[node-gyp](https://github.com/nodejs/node-gyp#installation) is the Node.js native addon build tool and it requires extra dependencies to be installed on Windows. In a command prompt which is run as administrator, run:
|
||||
[node-gyp](https://github.com/nodejs/node-gyp#installation) is the Node.js native addon build tool and it requires extra dependencies to be installed on Windows. In a command prompt which is run as administrator, run:
|
||||
|
||||
```
|
||||
npm --add-python-to-path='true' --debug install --global windows-build-tools
|
||||
@ -88,7 +88,7 @@ go get github.com/Unknwon/bra
|
||||
|
||||
bra run
|
||||
```
|
||||
You'll also need to run `grunt watch` to watch for changes to the front-end.
|
||||
You'll also need to run `npm run watch` to watch for changes to the front-end.
|
||||
|
||||
## Creating optimized release packages
|
||||
This step builds linux packages and requires that fpm is installed. Install fpm via `gem install fpm`.
|
||||
@ -119,7 +119,7 @@ Please contribute to the Grafana project and submit a pull request! Build new fe
|
||||
|
||||
**Problem**: When running `bra run` for the first time you get an error that it is not a recognized command.
|
||||
|
||||
**Solution**: Add the bin directory in your Go workspace directory to the path. Per default this is `$HOME/go/bin` on Linux and `%USERPROFILE%\go\bin` on Windows or `$GOPATH/bin` (`%GOPATH%\bin` on Windows) if you have set your own workspace directory.
|
||||
**Solution**: Add the bin directory in your Go workspace directory to the path. Per default this is `$HOME/go/bin` on Linux and `%USERPROFILE%\go\bin` on Windows or `$GOPATH/bin` (`%GOPATH%\bin` on Windows) if you have set your own workspace directory.
|
||||
<br><br>
|
||||
|
||||
**Problem**: When executing a `go get` command on Windows and you get an error about the git repository not existing.
|
||||
|
@ -1,23 +1,31 @@
|
||||
var webpack = require('webpack');
|
||||
var path = require('path');
|
||||
var webpackTestConfig = require('./scripts/webpack/webpack.test.js');
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
'use strict';
|
||||
|
||||
config.set({
|
||||
basePath: __dirname + '/public_gen',
|
||||
|
||||
frameworks: ['mocha', 'expect', 'sinon'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'vendor/npm/es6-shim/es6-shim.js',
|
||||
'vendor/npm/systemjs/dist/system.src.js',
|
||||
'test/test-main.js',
|
||||
|
||||
{pattern: '**/*.js', included: false},
|
||||
'public/test/index.ts',
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'public/test/index.ts': ['webpack', 'sourcemap'],
|
||||
},
|
||||
|
||||
webpack: webpackTestConfig,
|
||||
|
||||
webpackServer: {
|
||||
noInfo: true, // please don't spam the console when running in karma!
|
||||
},
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [],
|
||||
|
||||
reporters: ['dots'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
@ -26,8 +34,8 @@ module.exports = function(config) {
|
||||
browsers: ['PhantomJS'],
|
||||
captureTimeout: 20000,
|
||||
singleRun: true,
|
||||
autoWatchBatchDelay: 1000,
|
||||
browserNoActivityTimeout: 60000,
|
||||
// autoWatchBatchDelay: 1000,
|
||||
// browserNoActivityTimeout: 60000,
|
||||
});
|
||||
|
||||
};
|
||||
|
86
package.json
86
package.json
@ -10,14 +10,27 @@
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/d3": "^4.10.1",
|
||||
"@types/enzyme": "^2.8.9",
|
||||
"@types/node": "^8.0.31",
|
||||
"@types/react": "^16.0.5",
|
||||
"@types/react-dom": "^15.5.4",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"css-loader": "^0.28.7",
|
||||
"enzyme": "^3.0.0",
|
||||
"enzyme-adapter-react-16": "^1.0.0",
|
||||
"es6-promise": "^3.0.2",
|
||||
"es6-shim": "^0.35.3",
|
||||
"expect.js": "~0.2.0",
|
||||
"expose-loader": "^0.7.3",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"gaze": "^1.1.2",
|
||||
"glob": "~7.0.0",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt": "1.0.1",
|
||||
"grunt-angular-templates": "^1.1.0",
|
||||
"grunt-cli": "~1.2.0",
|
||||
"grunt-contrib-clean": "~1.0.0",
|
||||
@ -25,69 +38,86 @@
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.2",
|
||||
"grunt-contrib-htmlmin": "~2.0.0",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-contrib-uglify": "~2.0.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-exec": "^1.0.1",
|
||||
"grunt-filerev": "^2.3.1",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-karma": "~2.0.0",
|
||||
"grunt-ng-annotate": "^3.0.0",
|
||||
"grunt-notify": "^0.4.5",
|
||||
"grunt-postcss": "^0.8.0",
|
||||
"grunt-sass": "^2.0.0",
|
||||
"grunt-string-replace": "~1.3.1",
|
||||
"grunt-sass-lint": "^0.2.2",
|
||||
"grunt-usemin": "3.1.1",
|
||||
"grunt-webpack": "^3.0.2",
|
||||
"html-loader": "^0.5.1",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"jshint-stylish": "~2.2.1",
|
||||
"json-loader": "^0.5.7",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage": "1.1.1",
|
||||
"karma-expect": "~1.1.3",
|
||||
"karma-mocha": "~1.3.0",
|
||||
"karma-phantomjs-launcher": "1.0.4",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^2.0.4",
|
||||
"load-grunt-tasks": "3.5.2",
|
||||
"mocha": "3.5.0",
|
||||
"ng-annotate-loader": "^0.6.1",
|
||||
"ng-annotate-webpack-plugin": "^0.2.1-pre",
|
||||
"ngtemplate-loader": "^2.0.1",
|
||||
"phantomjs-prebuilt": "^2.1.15",
|
||||
"postcss-browser-reporter": "^0.5.0",
|
||||
"postcss-loader": "^2.0.6",
|
||||
"postcss-reporter": "^5.0.0",
|
||||
"react-test-renderer": "^16.0.0",
|
||||
"rxjs": "^5.4.3",
|
||||
"sass-lint": "^1.10.2",
|
||||
"systemjs": "0.20.19"
|
||||
"sass-loader": "^6.0.6",
|
||||
"sinon": "1.17.6",
|
||||
"systemjs": "0.20.19",
|
||||
"systemjs-plugin-css": "^0.1.36",
|
||||
"ts-loader": "^2.3.7",
|
||||
"tslint": "^5.7.0",
|
||||
"tslint-loader": "^3.5.3",
|
||||
"typescript": "^2.5.2",
|
||||
"webpack": "^3.6.0",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
"webpack-cleanup-plugin": "^0.5.1",
|
||||
"webpack-merge": "^4.1.0",
|
||||
"zone.js": "^0.7.2",
|
||||
"awesome-typescript-loader": "^3.2.3",
|
||||
"angular-mocks": "^1.6.6",
|
||||
"npm": "^5.4.2"
|
||||
},
|
||||
"scripts": {
|
||||
"webpack": "./node_modules/.bin/webpack --progress --colors --config scripts/webpack/webpack.dev.js",
|
||||
"watch": "./node_modules/.bin/webpack --progress --colors --watch --config scripts/webpack/webpack.dev.js",
|
||||
"build": "./node_modules/grunt-cli/bin/grunt",
|
||||
"test": "./node_modules/grunt-cli/bin/grunt test",
|
||||
"dev": "./node_modules/grunt-cli/bin/grunt && ./node_modules/grunt-cli/bin/grunt watch"
|
||||
"watch-test": "./node_modules/grunt-cli/bin/grunt karma:dev"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@types/enzyme": "^2.8.8",
|
||||
"ace-builds": "^1.2.8",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"jquery": "^3.2.1",
|
||||
"angular": "^1.6.6",
|
||||
"angular-bindonce": "^0.3.1",
|
||||
"angular-mocks": "^1.6.6",
|
||||
"angular-native-dragdrop": "^1.2.2",
|
||||
"angular-route": "^1.6.6",
|
||||
"angular-sanitize": "^1.6.6",
|
||||
"brace": "^0.10.0",
|
||||
"clipboard": "^1.7.1",
|
||||
"eventemitter3": "^2.0.2",
|
||||
"gaze": "^1.1.2",
|
||||
"grunt-jscs": "3.0.1",
|
||||
"grunt-sass-lint": "^0.2.2",
|
||||
"grunt-sync": "^0.6.2",
|
||||
"jquery": "^3.2.1",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"file-saver": "^1.3.3",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.18.1",
|
||||
"mousetrap": "^1.6.0",
|
||||
"ngreact": "^0.4.1",
|
||||
"react": "^15.6.1",
|
||||
"react-dom": "^15.6.1",
|
||||
"react-test-renderer": "^15.6.1",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"remarkable": "^1.7.1",
|
||||
"sinon": "1.17.6",
|
||||
"systemjs-builder": "^0.16.10",
|
||||
"tether": "^1.4.0",
|
||||
"tether-drop": "https://github.com/torkelo/drop",
|
||||
"tslint": "^5.7.0",
|
||||
"typescript": "^2.5.2",
|
||||
"virtual-scroll": "^1.1.1"
|
||||
"tether-drop": "https://github.com/torkelo/drop"
|
||||
}
|
||||
}
|
||||
|
@ -264,12 +264,16 @@ func applyCommandLineDefaultProperties(props map[string]string) {
|
||||
|
||||
func applyCommandLineProperties(props map[string]string) {
|
||||
for _, section := range Cfg.Sections() {
|
||||
sectionName := section.Name() + "."
|
||||
if section.Name() == ini.DEFAULT_SECTION {
|
||||
sectionName = ""
|
||||
}
|
||||
for _, key := range section.Keys() {
|
||||
keyString := fmt.Sprintf("%s.%s", section.Name(), key.Name())
|
||||
keyString := sectionName + key.Name()
|
||||
value, exists := props[keyString]
|
||||
if exists {
|
||||
key.SetValue(value)
|
||||
appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
|
||||
key.SetValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -449,16 +453,11 @@ func validateStaticRootPath() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(StaticRootPath, "css")); err == nil {
|
||||
return nil
|
||||
if _, err := os.Stat(path.Join(StaticRootPath, "build")); err != nil {
|
||||
logger.Error("Failed to detect generated javascript files in public/build")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(StaticRootPath + "_gen/css"); err == nil {
|
||||
StaticRootPath = StaticRootPath + "_gen"
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("Failed to detect generated css or javascript files in static root (%s), have you executed default grunt task?", StaticRootPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewConfigContext(args *CommandLineArgs) error {
|
||||
@ -656,4 +655,5 @@ func LogConfigurationInfo() {
|
||||
logger.Info("Path Data", "path", DataPath)
|
||||
logger.Info("Path Logs", "path", LogsPath)
|
||||
logger.Info("Path Plugins", "path", PluginsPath)
|
||||
logger.Info("App mode " + Env)
|
||||
}
|
||||
|
@ -1,23 +1,32 @@
|
||||
///<reference path="headers/common.d.ts" />
|
||||
|
||||
import 'bootstrap';
|
||||
import 'vendor/filesaver';
|
||||
import 'lodash-src';
|
||||
import 'angular-strap';
|
||||
import 'babel-polyfill';
|
||||
import 'file-saver';
|
||||
import 'lodash';
|
||||
import 'jquery';
|
||||
import 'angular';
|
||||
import 'angular-route';
|
||||
import 'angular-sanitize';
|
||||
import 'angular-dragdrop';
|
||||
import 'angular-native-dragdrop';
|
||||
import 'angular-bindonce';
|
||||
import 'angular-ui';
|
||||
import 'react';
|
||||
import 'react-dom';
|
||||
import 'ngreact';
|
||||
|
||||
import 'vendor/bootstrap/bootstrap';
|
||||
import 'vendor/angular-ui/ui-bootstrap-tpls';
|
||||
import 'vendor/angular-other/angular-strap';
|
||||
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
import config from 'app/core/config';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
// add move to lodash for backward compatabiltiy
|
||||
_.move = function (array, fromIndex, toIndex) {
|
||||
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
|
||||
return array;
|
||||
};
|
||||
|
||||
import {coreModule} from './core/core';
|
||||
|
||||
export class GrafanaApp {
|
||||
|
@ -1,19 +0,0 @@
|
||||
(function bootGrafana() {
|
||||
'use strict';
|
||||
|
||||
var systemLocate = System.locate;
|
||||
System.locate = function(load) {
|
||||
var System = this;
|
||||
return Promise.resolve(systemLocate.call(this, load)).then(function(address) {
|
||||
return address + System.cacheBust;
|
||||
});
|
||||
};
|
||||
System.cacheBust = '?bust=' + Date.now();
|
||||
|
||||
System.import('app/app').then(function(app) {
|
||||
app.default.init();
|
||||
}).catch(function(err) {
|
||||
console.log('Loading app module failed: ', err);
|
||||
});
|
||||
|
||||
})();
|
@ -15,16 +15,16 @@ export class PasswordStrength extends React.Component<IProps, any> {
|
||||
let strengthText = "strength: strong like a bull.";
|
||||
let strengthClass = "password-strength-good";
|
||||
|
||||
if (this.props.password.length < 4) {
|
||||
strengthText = "strength: weak sauce.";
|
||||
strengthClass = "password-strength-bad";
|
||||
}
|
||||
|
||||
if (this.props.password.length <= 8) {
|
||||
strengthText = "strength: you can do better.";
|
||||
strengthClass = "password-strength-ok";
|
||||
}
|
||||
|
||||
if (this.props.password.length < 4) {
|
||||
strengthText = "strength: weak sauce.";
|
||||
strengthClass = "password-strength-bad";
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`password-strength small ${strengthClass}`}>
|
||||
<em>{strengthText}</em>
|
||||
|
@ -26,60 +26,31 @@
|
||||
* Ctrl-Enter (Command-Enter): run onChange() function
|
||||
*/
|
||||
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
import _ from 'lodash';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import config from 'app/core/config';
|
||||
import ace from 'ace';
|
||||
import ace from 'brace';
|
||||
import './theme-grafana-dark';
|
||||
import 'brace/ext/language_tools';
|
||||
import 'brace/theme/textmate';
|
||||
import 'brace/mode/text';
|
||||
import 'brace/snippets/text';
|
||||
import 'brace/mode/sql';
|
||||
import 'brace/snippets/sql';
|
||||
|
||||
const ACE_SRC_BASE = "public/vendor/npm/ace-builds/src-noconflict/";
|
||||
const DEFAULT_THEME_DARK = "grafana-dark";
|
||||
const DEFAULT_THEME_LIGHT = "textmate";
|
||||
const DEFAULT_THEME_DARK = "ace/theme/grafana-dark";
|
||||
const DEFAULT_THEME_LIGHT = "ace/theme/textmate";
|
||||
const DEFAULT_MODE = "text";
|
||||
const DEFAULT_MAX_LINES = 10;
|
||||
const DEFAULT_TAB_SIZE = 2;
|
||||
const DEFAULT_BEHAVIOURS = true;
|
||||
|
||||
const GRAFANA_MODULES = ['theme-grafana-dark'];
|
||||
const GRAFANA_MODULE_BASE = "public/app/core/components/code_editor/";
|
||||
|
||||
// Trick for loading additional modules
|
||||
function setModuleUrl(moduleType, name, pluginBaseUrl = null) {
|
||||
let baseUrl = ACE_SRC_BASE;
|
||||
let aceModeName = `ace/${moduleType}/${name}`;
|
||||
let moduleName = `${moduleType}-${name}`;
|
||||
let componentName = `${moduleName}.js`;
|
||||
|
||||
if (_.includes(GRAFANA_MODULES, moduleName)) {
|
||||
baseUrl = GRAFANA_MODULE_BASE;
|
||||
}
|
||||
|
||||
if (pluginBaseUrl) {
|
||||
baseUrl = pluginBaseUrl + '/';
|
||||
}
|
||||
|
||||
if (moduleType === 'snippets') {
|
||||
componentName = `${moduleType}/${name}.js`;
|
||||
}
|
||||
|
||||
ace.config.setModuleUrl(aceModeName, baseUrl + componentName);
|
||||
}
|
||||
|
||||
setModuleUrl("ext", "language_tools");
|
||||
setModuleUrl("mode", "text");
|
||||
setModuleUrl("snippets", "text");
|
||||
|
||||
let editorTemplate = `<div></div>`;
|
||||
|
||||
function link(scope, elem, attrs) {
|
||||
let lightTheme = config.bootData.user.lightTheme;
|
||||
let default_theme = lightTheme ? DEFAULT_THEME_LIGHT : DEFAULT_THEME_DARK;
|
||||
|
||||
// Options
|
||||
let langMode = attrs.mode || DEFAULT_MODE;
|
||||
let maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
|
||||
let showGutter = attrs.showGutter !== undefined;
|
||||
let theme = attrs.theme || default_theme;
|
||||
let tabSize = attrs.tabSize || DEFAULT_TAB_SIZE;
|
||||
let behavioursEnabled = attrs.behavioursEnabled ? attrs.behavioursEnabled === 'true' : DEFAULT_BEHAVIOURS;
|
||||
|
||||
@ -103,10 +74,10 @@ function link(scope, elem, attrs) {
|
||||
// disable depreacation warning
|
||||
codeEditor.$blockScrolling = Infinity;
|
||||
// Padding hacks
|
||||
codeEditor.renderer.setScrollMargin(15, 15);
|
||||
(<any>codeEditor.renderer).setScrollMargin(15, 15);
|
||||
codeEditor.renderer.setPadding(10);
|
||||
|
||||
setThemeMode(theme);
|
||||
setThemeMode();
|
||||
setLangMode(langMode);
|
||||
setEditorContent(scope.content);
|
||||
|
||||
@ -162,44 +133,31 @@ function link(scope, elem, attrs) {
|
||||
});
|
||||
|
||||
function setLangMode(lang) {
|
||||
let aceModeName = `ace/mode/${lang}`;
|
||||
setModuleUrl("mode", lang, scope.datasource.meta.baseUrl || null);
|
||||
setModuleUrl("snippets", lang, scope.datasource.meta.baseUrl || null);
|
||||
editorSession.setMode(aceModeName);
|
||||
|
||||
ace.config.loadModule("ace/ext/language_tools", (language_tools) => {
|
||||
codeEditor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
enableSnippets: true
|
||||
});
|
||||
|
||||
if (scope.getCompleter()) {
|
||||
// make copy of array as ace seems to share completers array between instances
|
||||
codeEditor.completers = codeEditor.completers.slice();
|
||||
codeEditor.completers.push(scope.getCompleter());
|
||||
}
|
||||
ace.acequire("ace/ext/language_tools");
|
||||
codeEditor.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
enableSnippets: true
|
||||
});
|
||||
|
||||
if (scope.getCompleter()) {
|
||||
// make copy of array as ace seems to share completers array between instances
|
||||
const anyEditor = <any>codeEditor;
|
||||
anyEditor.completers = anyEditor.completers.slice();
|
||||
anyEditor.completers.push(scope.getCompleter());
|
||||
}
|
||||
|
||||
let aceModeName = `ace/mode/${lang}`;
|
||||
editorSession.setMode(aceModeName);
|
||||
}
|
||||
|
||||
function setThemeMode(theme) {
|
||||
setModuleUrl("theme", theme);
|
||||
let themeModule = `ace/theme/${theme}`;
|
||||
ace.config.loadModule(themeModule, (theme_module) => {
|
||||
// Check is theme light or dark and fix if needed
|
||||
let lightTheme = config.bootData.user.lightTheme;
|
||||
let fixedTheme = theme;
|
||||
if (lightTheme && theme_module.isDark) {
|
||||
fixedTheme = DEFAULT_THEME_LIGHT;
|
||||
} else if (!lightTheme && !theme_module.isDark) {
|
||||
fixedTheme = DEFAULT_THEME_DARK;
|
||||
}
|
||||
setModuleUrl("theme", fixedTheme);
|
||||
themeModule = `ace/theme/${fixedTheme}`;
|
||||
codeEditor.setTheme(themeModule);
|
||||
function setThemeMode() {
|
||||
let theme = DEFAULT_THEME_DARK;
|
||||
if (config.bootData.user.lightTheme) {
|
||||
theme = DEFAULT_THEME_LIGHT;
|
||||
}
|
||||
|
||||
elem.addClass("gf-code-editor--theme-loaded");
|
||||
});
|
||||
codeEditor.setTheme(theme);
|
||||
}
|
||||
|
||||
function setEditorContent(value) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* jshint ignore:start */
|
||||
|
||||
ace.define("ace/theme/grafana-dark",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
|
||||
ace.define("ace/theme/grafana-dark",["require","exports","module","ace/lib/dom"], function(acequire, exports, module) {
|
||||
"use strict";
|
||||
|
||||
exports.isDark = true;
|
||||
@ -109,7 +109,7 @@ ace.define("ace/theme/grafana-dark",["require","exports","module","ace/lib/dom"]
|
||||
background: url(data:image/png;base64,ivborw0kggoaaaansuheugaaaaeaaaaccayaaaczgbynaaaaekleqvqimwpq0fd0zxbzd/wpaajvaoxesgneaaaaaelftksuqmcc) right repeat-y\
|
||||
}";
|
||||
|
||||
var dom = require("../lib/dom");
|
||||
var dom = acequire("../lib/dom");
|
||||
dom.importCssString(exports.cssText, exports.cssClass);
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,3 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
///<reference path="./mod_defs.d.ts" />
|
||||
|
||||
import "./directives/dash_class";
|
||||
import "./directives/confirm_click";
|
||||
import "./directives/dash_edit_link";
|
||||
@ -11,7 +8,6 @@ import "./directives/ng_model_on_blur";
|
||||
import "./directives/spectrum_picker";
|
||||
import "./directives/tags";
|
||||
import "./directives/value_select_dropdown";
|
||||
import "./directives/plugin_component";
|
||||
import "./directives/rebuild_on_change";
|
||||
import "./directives/give_focus";
|
||||
import "./directives/diff-view";
|
||||
|
@ -1,4 +1,2 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
export default angular.module('grafana.core', ['ngRoute']);
|
||||
|
@ -1,7 +1,4 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
import coreModule from '../core_module';
|
||||
|
||||
function getBlockNodes(nodes) {
|
||||
@ -21,6 +18,7 @@ function getBlockNodes(nodes) {
|
||||
return blockNodes || nodes;
|
||||
}
|
||||
|
||||
/** @ngInject **/
|
||||
function rebuildOnChange($animate) {
|
||||
|
||||
return {
|
||||
|
@ -1,7 +1,7 @@
|
||||
define([
|
||||
'angular',
|
||||
'../core_module',
|
||||
'spectrum',
|
||||
'vendor/spectrum',
|
||||
],
|
||||
function (angular, coreModule) {
|
||||
'use strict';
|
||||
|
@ -2,7 +2,7 @@ define([
|
||||
'angular',
|
||||
'jquery',
|
||||
'../core_module',
|
||||
'bootstrap-tagsinput',
|
||||
'vendor/tagsinput/bootstrap-tagsinput.js',
|
||||
],
|
||||
function (angular, $, coreModule) {
|
||||
'use strict';
|
||||
|
@ -57,7 +57,8 @@ coreModule.filter('noXml', function() {
|
||||
};
|
||||
});
|
||||
|
||||
coreModule.filter('interpolateTemplateVars', function (templateSrv) {
|
||||
/** @ngInject */
|
||||
function interpolateTemplateVars(templateSrv) {
|
||||
var filterFunc: any = function(text, scope) {
|
||||
var scopedVars;
|
||||
if (scope.ctrl) {
|
||||
@ -71,6 +72,7 @@ coreModule.filter('interpolateTemplateVars', function (templateSrv) {
|
||||
|
||||
filterFunc.$stateful = true;
|
||||
return filterFunc;
|
||||
});
|
||||
}
|
||||
|
||||
coreModule.filter('interpolateTemplateVars', interpolateTemplateVars);
|
||||
export default {};
|
||||
|
@ -3,7 +3,7 @@
|
||||
import _ from 'lodash';
|
||||
import config from 'app/core/config';
|
||||
|
||||
import {Observable} from 'vendor/npm/rxjs/Observable';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
export class LiveSrv {
|
||||
conn: any;
|
||||
|
@ -1,2 +0,0 @@
|
||||
define([
|
||||
], function () {});
|
4
public/app/core/partials.ts
Normal file
4
public/app/core/partials.ts
Normal file
@ -0,0 +1,4 @@
|
||||
var templates = (<any>require).context('../', true, /\.html$/);
|
||||
templates.keys().forEach(function(key) {
|
||||
templates(key);
|
||||
});
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../headers/common.d.ts" />
|
||||
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
export class BundleLoader {
|
||||
lazy: any;
|
||||
|
||||
|
@ -1,18 +1,27 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import './dashboard_loaders';
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
import {BundleLoader} from './bundle_loader';
|
||||
|
||||
/** @ngInject **/
|
||||
function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
|
||||
var loadOrgBundle = new BundleLoader('app/features/org/all');
|
||||
var loadPluginsBundle = new BundleLoader('app/features/plugins/all');
|
||||
var loadAdminBundle = new BundleLoader('app/features/admin/admin');
|
||||
var loadAlertingBundle = new BundleLoader('app/features/alerting/all');
|
||||
var loadOrgBundle = {
|
||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
||||
return System.import('app/features/org/all');
|
||||
}]
|
||||
};
|
||||
|
||||
var loadAdminBundle = {
|
||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
||||
return System.import('app/features/admin/admin');
|
||||
}]
|
||||
};
|
||||
|
||||
var loadAlertingBundle = {
|
||||
lazy: ["$q", "$route", "$rootScope", ($q, $route, $rootScope) => {
|
||||
return System.import('app/features/alerting/all');
|
||||
}]
|
||||
};
|
||||
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
@ -47,19 +56,16 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
templateUrl: 'public/app/features/plugins/partials/ds_list.html',
|
||||
controller : 'DataSourcesCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/datasources/edit/:id', {
|
||||
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
||||
controller : 'DataSourceEditCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/datasources/new', {
|
||||
templateUrl: 'public/app/features/plugins/partials/ds_edit.html',
|
||||
controller : 'DataSourceEditCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/org', {
|
||||
templateUrl: 'public/app/features/org/partials/orgDetails.html',
|
||||
@ -175,19 +181,16 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
templateUrl: 'public/app/features/plugins/partials/plugin_list.html',
|
||||
controller: 'PluginListCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/plugins/:pluginId/edit', {
|
||||
templateUrl: 'public/app/features/plugins/partials/plugin_edit.html',
|
||||
controller: 'PluginEditCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/plugins/:pluginId/page/:slug', {
|
||||
templateUrl: 'public/app/features/plugins/partials/plugin_page.html',
|
||||
controller: 'AppPageCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
resolve: loadPluginsBundle,
|
||||
})
|
||||
.when('/styleguide/:page?', {
|
||||
controller: 'StyleGuideCtrl',
|
||||
|
@ -1,7 +1,6 @@
|
||||
define([
|
||||
'./alert_srv',
|
||||
'./util_srv',
|
||||
'./datasource_srv',
|
||||
'./context_srv',
|
||||
'./timer',
|
||||
'./keyboard_manager',
|
||||
|
@ -1,14 +1,25 @@
|
||||
// import React from 'react';
|
||||
// import {describe, beforeEach, it, sinon, expect} from 'test/lib/common';
|
||||
// import {shallow} from 'enzyme';
|
||||
//
|
||||
// import {PasswordStrength} from '../components/PasswordStrength';
|
||||
//
|
||||
// describe('PasswordStrength', () => {
|
||||
//
|
||||
// it.skip('should have class bad if length below 4', () => {
|
||||
// const wrapper = shallow(<PasswordStrength password="asd" />);
|
||||
// expect(wrapper.find(".password-strength-bad")).to.have.length(3);
|
||||
// });
|
||||
// });
|
||||
//
|
||||
import React from 'react';
|
||||
import {describe, it, expect} from 'test/lib/common';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import {PasswordStrength} from '../components/PasswordStrength';
|
||||
|
||||
describe('PasswordStrength', () => {
|
||||
|
||||
it('should have class bad if length below 4', () => {
|
||||
const wrapper = shallow(<PasswordStrength password="asd" />);
|
||||
expect(wrapper.find(".password-strength-bad")).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should have class ok if length below 8', () => {
|
||||
const wrapper = shallow(<PasswordStrength password="asdasd" />);
|
||||
expect(wrapper.find(".password-strength-ok")).to.have.length(1);
|
||||
});
|
||||
|
||||
it('should have class good if length above 8', () => {
|
||||
const wrapper = shallow(<PasswordStrength password="asdaasdda" />);
|
||||
expect(wrapper.find(".password-strength-good")).to.have.length(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
29
public/app/core/specs/backend_srv_specs.ts
Normal file
29
public/app/core/specs/backend_srv_specs.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
|
||||
import 'app/core/services/backend_srv';
|
||||
|
||||
describe('backend_srv', function() {
|
||||
var _backendSrv;
|
||||
var _http;
|
||||
var _httpBackend;
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.inject(function ($httpBackend, $http, backendSrv) {
|
||||
_httpBackend = $httpBackend;
|
||||
_http = $http;
|
||||
_backendSrv = backendSrv;
|
||||
}));
|
||||
|
||||
describe('when handling errors', function() {
|
||||
it('should return the http status code', function(done) {
|
||||
_httpBackend.whenGET('gateway-error').respond(502);
|
||||
_backendSrv.datasourceRequest({
|
||||
url: 'gateway-error'
|
||||
}).catch(function(err) {
|
||||
expect(err.status).to.be(502);
|
||||
done();
|
||||
});
|
||||
_httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
167
public/app/core/specs/value_select_dropdown_specs.ts
Normal file
167
public/app/core/specs/value_select_dropdown_specs.ts
Normal file
@ -0,0 +1,167 @@
|
||||
import {describe, beforeEach, it, expect, angularMocks, sinon} from 'test/lib/common';
|
||||
import 'app/core/directives/value_select_dropdown';
|
||||
|
||||
describe("SelectDropdownCtrl", function() {
|
||||
var scope;
|
||||
var ctrl;
|
||||
var tagValuesMap: any = {};
|
||||
var rootScope;
|
||||
var q;
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.inject(function($controller, $rootScope, $q, $httpBackend) {
|
||||
rootScope = $rootScope;
|
||||
q = $q;
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('ValueSelectDropdownCtrl', {$scope: scope});
|
||||
ctrl.onUpdated = sinon.spy();
|
||||
$httpBackend.when('GET', /\.html$/).respond('');
|
||||
}));
|
||||
|
||||
describe("Given simple variable", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'hej', value: 'hej' },
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
};
|
||||
ctrl.init();
|
||||
});
|
||||
|
||||
it("Should init labelText and linkText", function() {
|
||||
expect(ctrl.linkText).to.be("hej");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given variable with tags and dropdown is opened", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'server-1', value: 'server-1'},
|
||||
options: [
|
||||
{text: 'server-1', value: 'server-1', selected: true},
|
||||
{text: 'server-2', value: 'server-2'},
|
||||
{text: 'server-3', value: 'server-3'},
|
||||
],
|
||||
tags: ["key1", "key2", "key3"],
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
multi: true
|
||||
};
|
||||
tagValuesMap.key1 = ['server-1', 'server-3'];
|
||||
tagValuesMap.key2 = ['server-2', 'server-3'];
|
||||
tagValuesMap.key3 = ['server-1', 'server-2', 'server-3'];
|
||||
ctrl.init();
|
||||
ctrl.show();
|
||||
});
|
||||
|
||||
it("should init tags model", function() {
|
||||
expect(ctrl.tags.length).to.be(3);
|
||||
expect(ctrl.tags[0].text).to.be("key1");
|
||||
});
|
||||
|
||||
it("should init options model", function() {
|
||||
expect(ctrl.options.length).to.be(3);
|
||||
});
|
||||
|
||||
it("should init selected values array", function() {
|
||||
expect(ctrl.selectedValues.length).to.be(1);
|
||||
});
|
||||
|
||||
it("should set linkText", function() {
|
||||
expect(ctrl.linkText).to.be('server-1');
|
||||
});
|
||||
|
||||
describe('after adititional value is selected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectValue(ctrl.options[2], {});
|
||||
ctrl.commitChanges();
|
||||
});
|
||||
|
||||
it('should update link text', function() {
|
||||
expect(ctrl.linkText).to.be('server-1 + server-3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When tag is selected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectTag(ctrl.tags[0]);
|
||||
rootScope.$digest();
|
||||
ctrl.commitChanges();
|
||||
});
|
||||
|
||||
it("should select tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(1);
|
||||
});
|
||||
|
||||
it("should select values", function() {
|
||||
expect(ctrl.options[0].selected).to.be(true);
|
||||
expect(ctrl.options[2].selected).to.be(true);
|
||||
});
|
||||
|
||||
it("link text should not include tag values", function() {
|
||||
expect(ctrl.linkText).to.be('');
|
||||
});
|
||||
|
||||
describe('and then dropdown is opened and closed without changes', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.show();
|
||||
ctrl.commitChanges();
|
||||
rootScope.$digest();
|
||||
});
|
||||
|
||||
it("should still have selected tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and then unselected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectTag(ctrl.tags[0]);
|
||||
rootScope.$digest();
|
||||
});
|
||||
|
||||
it("should deselect tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and then value is unselected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectValue(ctrl.options[0], {});
|
||||
});
|
||||
|
||||
it("should deselect tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given variable with selected tags", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'server-1', value: 'server-1', tags: [{text: 'key1', selected: true}] },
|
||||
options: [
|
||||
{text: 'server-1', value: 'server-1'},
|
||||
{text: 'server-2', value: 'server-2'},
|
||||
{text: 'server-3', value: 'server-3'},
|
||||
],
|
||||
tags: ["key1", "key2", "key3"],
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
multi: true
|
||||
};
|
||||
ctrl.init();
|
||||
ctrl.show();
|
||||
});
|
||||
|
||||
it("should set tag as selected", function() {
|
||||
expect(ctrl.tags[0].selected).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,7 @@ define([
|
||||
'./dashlinks/module',
|
||||
'./annotations/all',
|
||||
'./templating/all',
|
||||
'./plugins/all',
|
||||
'./dashboard/all',
|
||||
'./playlist/all',
|
||||
'./snapshot/all',
|
||||
|
@ -9,7 +9,8 @@ var template = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
coreModule.directive('dashRepeatOption', function(variableSrv) {
|
||||
/** @ngInject **/
|
||||
function dashRepeatOptionDirective(variableSrv) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: template,
|
||||
@ -30,5 +31,6 @@ coreModule.directive('dashRepeatOption', function(variableSrv) {
|
||||
scope.variables.unshift({text: 'Disabled', value: null});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
coreModule.directive('dashRepeatOption', dashRepeatOptionDirective);
|
||||
|
@ -113,7 +113,8 @@ export class DashRowCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.directive('dashRow', function($rootScope) {
|
||||
/** @ngInject */
|
||||
function dashRowDirective($rootScope) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/row/row.html',
|
||||
@ -142,9 +143,10 @@ coreModule.directive('dashRow', function($rootScope) {
|
||||
}, scope);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
coreModule.directive('panelWidth', function($rootScope) {
|
||||
/** @ngInject */
|
||||
function panelWidthDirective($rootScope) {
|
||||
return function(scope, element) {
|
||||
var fullscreen = false;
|
||||
|
||||
@ -180,10 +182,10 @@ coreModule.directive('panelWidth', function($rootScope) {
|
||||
element.hide();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
coreModule.directive('panelDropZone', function($timeout) {
|
||||
/** @ngInject */
|
||||
function panelDropZoneDirective($timeout) {
|
||||
return function(scope, element) {
|
||||
var row = scope.ctrl.row;
|
||||
var indrag = false;
|
||||
@ -237,5 +239,9 @@ coreModule.directive('panelDropZone', function($timeout) {
|
||||
|
||||
updateState();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
coreModule.directive('dashRow', dashRowDirective);
|
||||
coreModule.directive('panelWidth', panelWidthDirective);
|
||||
coreModule.directive('panelDropZone', panelDropZoneDirective);
|
||||
|
||||
|
110
public/app/features/dashboard/specs/share_modal_ctrl_specs.ts
Normal file
110
public/app/features/dashboard/specs/share_modal_ctrl_specs.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import {describe, beforeEach, it, expect, sinon, angularMocks} from 'test/lib/common';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import '../shareModalCtrl';
|
||||
import config from 'app/core/config';
|
||||
import 'app/features/panellinks/linkSrv';
|
||||
|
||||
describe('ShareModalCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
|
||||
function setTime(range) {
|
||||
ctx.timeSrv.timeRange = sinon.stub().returns(range);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
setTime({ from: new Date(1000), to: new Date(2000) });
|
||||
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.module(function($compileProvider) {
|
||||
$compileProvider.preAssignBindingsEnabled(true);
|
||||
}));
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
|
||||
beforeEach(ctx.createControllerPhase('ShareModalCtrl'));
|
||||
|
||||
describe('shareUrl with current time range and panel', function() {
|
||||
it('should generate share url absolute time', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&panelId=22&fullscreen');
|
||||
});
|
||||
|
||||
it('should generate render url', function() {
|
||||
ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/db/my-dash';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/dashboard-solo/db/my-dash';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).to.contain(base + params);
|
||||
});
|
||||
|
||||
it('should remove panel id when no panel in scope', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.forCurrent = true;
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1');
|
||||
});
|
||||
|
||||
it('should add theme when specified', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.theme = 'light';
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
|
||||
});
|
||||
|
||||
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?fullscreen&edit');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?fullscreen&edit&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
|
||||
});
|
||||
|
||||
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?edit&fullscreen');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?edit&fullscreen&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
|
||||
});
|
||||
|
||||
it('should include template variables in url', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.includeTemplateVars = true;
|
||||
|
||||
ctx.templateSrv.fillVariableValuesForUrl = function(params) {
|
||||
params['var-app'] = 'mupp';
|
||||
params['var-server'] = 'srv-01';
|
||||
};
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&var-app=mupp&var-server=srv-01');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,82 @@
|
||||
import {describe, beforeEach, it, expect, sinon, angularMocks} from 'test/lib/common';
|
||||
import 'app/features/dashboard/unsavedChangesSrv';
|
||||
import 'app/features/dashboard/dashboard_srv';
|
||||
|
||||
describe("unsavedChangesSrv", function() {
|
||||
var _unsavedChangesSrv;
|
||||
var _dashboardSrv;
|
||||
var _location;
|
||||
var _contextSrvStub = { isEditor: true };
|
||||
var _rootScope;
|
||||
var tracker;
|
||||
var dash;
|
||||
var scope;
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.module(function($provide) {
|
||||
$provide.value('contextSrv', _contextSrvStub);
|
||||
$provide.value('$window', {});
|
||||
}));
|
||||
|
||||
beforeEach(angularMocks.inject(function(unsavedChangesSrv, $location, $rootScope, dashboardSrv) {
|
||||
_unsavedChangesSrv = unsavedChangesSrv;
|
||||
_dashboardSrv = dashboardSrv;
|
||||
_location = $location;
|
||||
_rootScope = $rootScope;
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
dash = _dashboardSrv.create({
|
||||
refresh: false,
|
||||
rows: [
|
||||
{
|
||||
panels: [{ test: "asd", legend: { } }]
|
||||
}
|
||||
]
|
||||
});
|
||||
scope = _rootScope.$new();
|
||||
scope.appEvent = sinon.spy();
|
||||
scope.onAppEvent = sinon.spy();
|
||||
|
||||
tracker = new _unsavedChangesSrv.Tracker(dash, scope);
|
||||
});
|
||||
|
||||
it('No changes should not have changes', function() {
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Simple change should be registered', function() {
|
||||
dash.property = "google";
|
||||
expect(tracker.hasChanges()).to.be(true);
|
||||
});
|
||||
|
||||
it('Should ignore a lot of changes', function() {
|
||||
dash.time = {from: '1h'};
|
||||
dash.refresh = true;
|
||||
dash.schemaVersion = 10;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore row collapse change', function() {
|
||||
dash.rows[0].collapse = true;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore panel legend changes', function() {
|
||||
dash.rows[0].panels[0].legend.sortDesc = true;
|
||||
dash.rows[0].panels[0].legend.sort = "avg";
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore panel repeats', function() {
|
||||
dash.rows[0].panels.push({repeatPanelId: 10});
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore row repeats', function() {
|
||||
dash.addEmptyRow();
|
||||
dash.rows[1].repeatRowId = 10;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
});
|
53
public/app/features/dashboard/specs/viewstate_srv_specs.ts
Normal file
53
public/app/features/dashboard/specs/viewstate_srv_specs.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
|
||||
import 'app/features/dashboard/viewStateSrv';
|
||||
import config from 'app/core/config';
|
||||
|
||||
describe('when updating view state', function() {
|
||||
var viewState, location;
|
||||
var timeSrv = {};
|
||||
var templateSrv = {};
|
||||
var contextSrv = {
|
||||
user: {
|
||||
orgId: 19
|
||||
}
|
||||
};
|
||||
beforeEach(function() {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.module(function($provide) {
|
||||
$provide.value('timeSrv', timeSrv);
|
||||
$provide.value('templateSrv', templateSrv);
|
||||
$provide.value('contextSrv', contextSrv);
|
||||
}));
|
||||
|
||||
beforeEach(angularMocks.inject(function(dashboardViewStateSrv, $location, $rootScope) {
|
||||
$rootScope.onAppEvent = function() {};
|
||||
$rootScope.dashboard = {meta: {}};
|
||||
viewState = dashboardViewStateSrv.create($rootScope);
|
||||
location = $location;
|
||||
}));
|
||||
|
||||
describe('to fullscreen true and edit true', function() {
|
||||
it('should update querystring and view state', function() {
|
||||
var updateState = {fullscreen: true, edit: true, panelId: 1};
|
||||
viewState.update(updateState);
|
||||
expect(location.search()).to.eql({fullscreen: true, edit: true, panelId: 1, orgId: 1});
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(true);
|
||||
expect(viewState.state.fullscreen).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('to fullscreen false', function() {
|
||||
it('should remove params from query string', function() {
|
||||
viewState.update({fullscreen: true, panelId: 1, edit: true});
|
||||
viewState.update({fullscreen: false});
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(false);
|
||||
expect(viewState.state.fullscreen).to.be(null);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
import {coreModule} from 'app/core/core';
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {PanelCtrl} from './panel_ctrl';
|
||||
import {PanelCtrl} from 'app/features/panel/panel_ctrl';
|
||||
|
||||
import * as rangeUtil from 'app/core/utils/rangeutil';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import config from 'app/core/config';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
|
46
public/app/features/panellinks/specs/link_srv_specs.ts
Normal file
46
public/app/features/panellinks/specs/link_srv_specs.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
|
||||
import 'app/features/panellinks/linkSrv';
|
||||
import _ from 'lodash';
|
||||
|
||||
describe('linkSrv', function() {
|
||||
var _linkSrv;
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
|
||||
beforeEach(angularMocks.inject(function(linkSrv) {
|
||||
_linkSrv = linkSrv;
|
||||
}));
|
||||
|
||||
describe('when appending query strings', function() {
|
||||
|
||||
it('add ? to URL if not present', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com', 'foo=bar');
|
||||
expect(url).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
|
||||
it('do not add & to URL if ? is present but query string is empty', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com?', 'foo=bar');
|
||||
expect(url).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
|
||||
it('add & to URL if query string is present', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com?foo=bar', 'hello=world');
|
||||
expect(url).to.be('http://example.com?foo=bar&hello=world');
|
||||
});
|
||||
|
||||
it('do not change the URL if there is nothing to append', function() {
|
||||
_.each(['', undefined, null], function(toAppend) {
|
||||
var url1 = _linkSrv.appendToQueryString('http://example.com', toAppend);
|
||||
expect(url1).to.be('http://example.com');
|
||||
|
||||
var url2 = _linkSrv.appendToQueryString('http://example.com?', toAppend);
|
||||
expect(url2).to.be('http://example.com?');
|
||||
|
||||
var url3 = _linkSrv.appendToQueryString('http://example.com?foo=bar', toAppend);
|
||||
expect(url3).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -4,3 +4,5 @@ import './plugin_list_ctrl';
|
||||
import './import_list/import_list';
|
||||
import './ds_edit_ctrl';
|
||||
import './ds_list_ctrl';
|
||||
import './datasource_srv';
|
||||
import './plugin_component';
|
||||
|
@ -1,10 +1,11 @@
|
||||
define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'../core_module',
|
||||
'app/core/core_module',
|
||||
'app/core/config',
|
||||
'./plugin_loader',
|
||||
],
|
||||
function (angular, _, coreModule, config) {
|
||||
function (angular, _, coreModule, config, pluginLoader) {
|
||||
'use strict';
|
||||
|
||||
coreModule.default.service('datasourceSrv', function($q, $injector, $rootScope, templateSrv) {
|
||||
@ -41,8 +42,7 @@ function (angular, _, coreModule, config) {
|
||||
var deferred = $q.defer();
|
||||
var pluginDef = dsConfig.meta;
|
||||
|
||||
System.import(pluginDef.module).then(function(plugin) {
|
||||
console.log(plugin);
|
||||
pluginLoader.importPluginModule(pluginDef.module).then(function(plugin) {
|
||||
// check if its in cache now
|
||||
if (self.datasources[name]) {
|
||||
deferred.resolve(self.datasources[name]);
|
@ -1,10 +1,10 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import {importPluginModule} from './plugin_loader';
|
||||
|
||||
import {UnknownPanelCtrl} from 'app/plugins/panel/unknown/module';
|
||||
|
||||
/** @ngInject **/
|
||||
@ -64,7 +64,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
let panelInfo = config.panels[scope.panel.type];
|
||||
var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
|
||||
if (panelInfo) {
|
||||
panelCtrlPromise = System.import(panelInfo.module).then(function(panelModule) {
|
||||
panelCtrlPromise = importPluginModule(panelInfo.module).then(function(panelModule) {
|
||||
return panelModule.PanelCtrl;
|
||||
});
|
||||
}
|
||||
@ -104,7 +104,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
return datasourceSrv.get(datasource).then(ds => {
|
||||
scope.datasource = ds;
|
||||
|
||||
return System.import(ds.meta.module).then(dsModule => {
|
||||
return importPluginModule(ds.meta.module).then(dsModule => {
|
||||
return {
|
||||
baseUrl: ds.meta.baseUrl,
|
||||
name: 'query-ctrl-' + ds.meta.id,
|
||||
@ -118,7 +118,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
// QueryOptionsCtrl
|
||||
case "query-options-ctrl": {
|
||||
return datasourceSrv.get(scope.ctrl.panel.datasource).then(ds => {
|
||||
return System.import(ds.meta.module).then((dsModule): any => {
|
||||
return importPluginModule(ds.meta.module).then((dsModule): any => {
|
||||
if (!dsModule.QueryOptionsCtrl) {
|
||||
return {notFound: true};
|
||||
}
|
||||
@ -135,7 +135,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
}
|
||||
// Annotations
|
||||
case "annotations-query-ctrl": {
|
||||
return System.import(scope.ctrl.currentDatasource.meta.module).then(function(dsModule) {
|
||||
return importPluginModule(scope.ctrl.currentDatasource.meta.module).then(function(dsModule) {
|
||||
return {
|
||||
baseUrl: scope.ctrl.currentDatasource.meta.baseUrl,
|
||||
name: 'annotations-query-ctrl-' + scope.ctrl.currentDatasource.meta.id,
|
||||
@ -148,7 +148,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
// Datasource ConfigCtrl
|
||||
case 'datasource-config-ctrl': {
|
||||
var dsMeta = scope.ctrl.datasourceMeta;
|
||||
return System.import(dsMeta.module).then(function(dsModule): any {
|
||||
return importPluginModule(dsMeta.module).then(function(dsModule): any {
|
||||
if (!dsModule.ConfigCtrl) {
|
||||
return {notFound: true};
|
||||
}
|
||||
@ -165,7 +165,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
// AppConfigCtrl
|
||||
case 'app-config-ctrl': {
|
||||
let model = scope.ctrl.model;
|
||||
return System.import(model.module).then(function(appModule) {
|
||||
return importPluginModule(model.module).then(function(appModule) {
|
||||
return {
|
||||
baseUrl: model.baseUrl,
|
||||
name: 'app-config-' + model.id,
|
||||
@ -178,7 +178,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
||||
// App Page
|
||||
case 'app-page': {
|
||||
let appModel = scope.ctrl.appModel;
|
||||
return System.import(appModel.module).then(function(appModule) {
|
||||
return importPluginModule(appModel.module).then(function(appModule) {
|
||||
return {
|
||||
baseUrl: appModel.baseUrl,
|
||||
name: 'app-page-' + appModel.id + '-' + scope.ctrl.page.slug,
|
120
public/app/features/plugins/plugin_loader.ts
Normal file
120
public/app/features/plugins/plugin_loader.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import System from 'systemjs/dist/system.js';
|
||||
import _ from 'lodash';
|
||||
import * as sdk from 'app/plugins/sdk';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import moment from 'moment';
|
||||
import angular from 'angular';
|
||||
import jquery from 'jquery';
|
||||
import config from 'app/core/config';
|
||||
import TimeSeries from 'app/core/time_series2';
|
||||
|
||||
import * as graphitePlugin from 'app/plugins/datasource/graphite/module';
|
||||
import * as cloudwatchPlugin from 'app/plugins/datasource/cloudwatch/module';
|
||||
import * as elasticsearchPlugin from 'app/plugins/datasource/elasticsearch/module';
|
||||
import * as opentsdbPlugin from 'app/plugins/datasource/opentsdb/module';
|
||||
import * as grafanaPlugin from 'app/plugins/datasource/grafana/module';
|
||||
import * as influxdbPlugin from 'app/plugins/datasource/influxdb/module';
|
||||
import * as mixedPlugin from 'app/plugins/datasource/mixed/module';
|
||||
import * as mysqlPlugin from 'app/plugins/datasource/mysql/module';
|
||||
import * as prometheusPlugin from 'app/plugins/datasource/prometheus/module';
|
||||
|
||||
import * as textPanel from 'app/plugins/panel/text/module';
|
||||
import * as graphPanel from 'app/plugins/panel/graph/module';
|
||||
import * as dashListPanel from 'app/plugins/panel/dashlist/module';
|
||||
import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
|
||||
import * as alertListPanel from 'app/plugins/panel/alertlist/module';
|
||||
import * as heatmapPanel from 'app/plugins/panel/heatmap/module';
|
||||
import * as tablePanel from 'app/plugins/panel/table/module';
|
||||
import * as singlestatPanel from 'app/plugins/panel/singlestat/module';
|
||||
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
|
||||
import * as testDataAppPlugin from 'app/plugins/app/testdata/module';
|
||||
import * as testDataDSPlugin from 'app/plugins/app/testdata/datasource/module';
|
||||
|
||||
let builtInPlugins = {
|
||||
"app/plugins/datasource/graphite/module": graphitePlugin,
|
||||
"app/plugins/datasource/cloudwatch/module": cloudwatchPlugin,
|
||||
"app/plugins/datasource/elasticsearch/module": elasticsearchPlugin,
|
||||
"app/plugins/datasource/opentsdb/module": opentsdbPlugin,
|
||||
"app/plugins/datasource/grafana/module": grafanaPlugin,
|
||||
"app/plugins/datasource/influxdb/module": influxdbPlugin,
|
||||
"app/plugins/datasource/mixed/module": mixedPlugin,
|
||||
"app/plugins/datasource/mysql/module": mysqlPlugin,
|
||||
"app/plugins/datasource/prometheus/module": prometheusPlugin,
|
||||
"app/plugins/app/testdata/module": testDataAppPlugin,
|
||||
"app/plugins/app/testdata/datasource/module": testDataDSPlugin,
|
||||
|
||||
"app/plugins/panel/text/module": textPanel,
|
||||
"app/plugins/panel/graph/module": graphPanel,
|
||||
"app/plugins/panel/dashlist/module": dashListPanel,
|
||||
"app/plugins/panel/pluginlist/module": pluginsListPanel,
|
||||
"app/plugins/panel/alertlist/module": alertListPanel,
|
||||
"app/plugins/panel/heatmap/module": heatmapPanel,
|
||||
"app/plugins/panel/table/module": tablePanel,
|
||||
"app/plugins/panel/singlestat/module": singlestatPanel,
|
||||
"app/plugins/panel/gettingstarted/module": gettingStartedPanel,
|
||||
};
|
||||
|
||||
System.config({
|
||||
baseURL: 'public',
|
||||
defaultExtension: 'js',
|
||||
packages: {
|
||||
'plugins': {
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
},
|
||||
map: {
|
||||
text: 'vendor/plugin-text/text.js',
|
||||
css: 'vendor/plugin-css/css.js'
|
||||
},
|
||||
});
|
||||
|
||||
// add cache busting
|
||||
var systemLocate = System.locate;
|
||||
System.cacheBust = '?bust=' + Date.now();
|
||||
System.locate = function(load) {
|
||||
var System = this;
|
||||
return Promise.resolve(systemLocate.call(this, load)).then(function(address) {
|
||||
return address + System.cacheBust;
|
||||
});
|
||||
};
|
||||
|
||||
System.registerDynamic('lodash', [], true, function(require, exports, module) { module.exports = _; });
|
||||
System.registerDynamic('moment', [], true, function(require, exports, module) { module.exports = moment; });
|
||||
System.registerDynamic('jquery', [], true, function(require, exports, module) { module.exports = jquery; });
|
||||
System.registerDynamic('angular', [], true, function(require, exports, module) { module.exports = angular; });
|
||||
System.registerDynamic('app/plugins/sdk', [], true, function(require, exports, module) { module.exports = sdk; });
|
||||
System.registerDynamic('app/core/utils/kbn', [], true, function(require, exports, module) { module.exports = kbn; });
|
||||
System.registerDynamic('app/core/config', [], true, function(require, exports, module) { module.exports = config; });
|
||||
System.registerDynamic('app/core/time_series', [], true, function(require, exports, module) { module.exports = TimeSeries; });
|
||||
System.registerDynamic('app/core/time_series2', [], true, function(require, exports, module) { module.exports = TimeSeries; });
|
||||
|
||||
import 'vendor/flot/jquery.flot';
|
||||
import 'vendor/flot/jquery.flot.selection';
|
||||
import 'vendor/flot/jquery.flot.time';
|
||||
import 'vendor/flot/jquery.flot.stack';
|
||||
import 'vendor/flot/jquery.flot.pie';
|
||||
import 'vendor/flot/jquery.flot.stackpercent';
|
||||
import 'vendor/flot/jquery.flot.fillbelow';
|
||||
import 'vendor/flot/jquery.flot.crosshair';
|
||||
import 'vendor/flot/jquery.flot.dashes';
|
||||
|
||||
for (let flotDep of ['jquery.flot', 'jquery.flot.pie', 'jquery.flot.time']) {
|
||||
System.registerDynamic(flotDep, [], true, function(require, exports, module) { module.exports = {fakeDep: 1}; });
|
||||
}
|
||||
|
||||
export function importPluginModule(path: string): Promise<any> {
|
||||
let builtIn = builtInPlugins[path];
|
||||
if (builtIn) {
|
||||
return Promise.resolve(builtIn);
|
||||
}
|
||||
return System.import(path);
|
||||
}
|
||||
|
||||
export function loadPluginCss(options) {
|
||||
if (config.bootData.user.lightTheme) {
|
||||
System.import(options.light + '!css');
|
||||
} else {
|
||||
System.import(options.dark + '!css');
|
||||
}
|
||||
}
|
||||
|
60
public/app/features/plugins/specs/datasource_srv_specs.ts
Normal file
60
public/app/features/plugins/specs/datasource_srv_specs.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import {describe, beforeEach, it, expect, angularMocks} from 'test/lib/common';
|
||||
import config from 'app/core/config';
|
||||
import 'app/features/plugins/datasource_srv';
|
||||
|
||||
describe('datasource_srv', function() {
|
||||
var _datasourceSrv;
|
||||
var metricSources;
|
||||
var templateSrv = {};
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.module(function($provide) {
|
||||
$provide.value('templateSrv', templateSrv);
|
||||
}));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.inject(function(datasourceSrv) {
|
||||
_datasourceSrv = datasourceSrv;
|
||||
}));
|
||||
|
||||
describe('when loading metric sources', function() {
|
||||
var unsortedDatasources = {
|
||||
'mmm': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
'--Grafana--': {
|
||||
type: 'grafana',
|
||||
meta: {builtIn: true, metrics: {m: 1}, id: "grafana"}
|
||||
},
|
||||
'--Mixed--': {
|
||||
type: 'test-db',
|
||||
meta: {builtIn: true, metrics: {m: 1}, id: "mixed"}
|
||||
},
|
||||
'ZZZ': {
|
||||
type: 'test-db',
|
||||
meta: {metrics: {m: 1} }
|
||||
},
|
||||
'aaa': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
'BBB': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
};
|
||||
beforeEach(function() {
|
||||
config.datasources = unsortedDatasources;
|
||||
metricSources = _datasourceSrv.getMetricSources({skipVariables: true});
|
||||
});
|
||||
|
||||
it('should return a list of sources sorted case insensitively with builtin sources last', function() {
|
||||
expect(metricSources[0].name).to.be('aaa');
|
||||
expect(metricSources[1].name).to.be('BBB');
|
||||
expect(metricSources[2].name).to.be('mmm');
|
||||
expect(metricSources[3].name).to.be('ZZZ');
|
||||
expect(metricSources[4].name).to.be('--Grafana--');
|
||||
expect(metricSources[5].name).to.be('--Mixed--');
|
||||
});
|
||||
});
|
||||
});
|
@ -36,7 +36,7 @@ class StyleGuideCtrl {
|
||||
}
|
||||
|
||||
loadColors() {
|
||||
this.$http.get('public/sass/styleguide.json').then(res => {
|
||||
this.$http.get('public/build/styleguide.json').then(res => {
|
||||
this.colors = _.map(res.data[this.theme], (value, key) => {
|
||||
return {name: key, value: value};
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ describe('templateSrv', function() {
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.module($provide => {
|
||||
$provide.value('timeSrv', {});
|
||||
$provide.value('datasourceSrv', {});
|
||||
}));
|
||||
|
||||
beforeEach(angularMocks.inject(function(variableSrv, templateSrv) {
|
||||
|
1
public/app/headers/common.d.ts
vendored
1
public/app/headers/common.d.ts
vendored
@ -71,3 +71,4 @@ declare module 'ace' {
|
||||
var ace: any;
|
||||
export default ace;
|
||||
}
|
||||
|
||||
|
3
public/app/index.ts
Normal file
3
public/app/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import app from './app';
|
||||
app.init();
|
||||
|
@ -5,7 +5,7 @@ import './metric_agg';
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import queryDef from './query_def';
|
||||
import * as queryDef from './query_def';
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
|
||||
export class ElasticQueryCtrl extends QueryCtrl {
|
||||
|
@ -5,6 +5,8 @@ import angular from 'angular';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import {ElasticDatasource} from "../datasource";
|
||||
|
||||
console.log(ElasticDatasource);
|
||||
|
||||
describe('ElasticDatasource', function() {
|
||||
var ctx = new helpers.ServiceTestContext();
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import './add_graphite_func';
|
||||
import './func_editor';
|
||||
|
||||
|
@ -9,7 +9,7 @@ describe('graphiteDatasource', function() {
|
||||
|
||||
beforeEach(angularMocks.module('grafana.core'));
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(ctx.providePhase(['backendSrv']));
|
||||
beforeEach(ctx.providePhase(['backendSrv', 'templateSrv']));
|
||||
beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
|
||||
ctx.$q = $q;
|
||||
ctx.$httpBackend = $httpBackend;
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
import '../query_ctrl';
|
||||
import 'app/core/services/segment_srv';
|
||||
import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
|
||||
|
||||
|
@ -1,106 +0,0 @@
|
||||
// jshint ignore: start
|
||||
// jscs: disable
|
||||
|
||||
ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
|
||||
|
||||
var SqlHighlightRules = function() {
|
||||
|
||||
var keywords = (
|
||||
"select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|" +
|
||||
"when|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|" +
|
||||
"foreign|not|references|default|null|inner|cross|natural|database|drop|grant"
|
||||
);
|
||||
|
||||
var builtinConstants = (
|
||||
"true|false"
|
||||
);
|
||||
|
||||
var builtinFunctions = (
|
||||
"avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|" +
|
||||
"coalesce|ifnull|isnull|nvl"
|
||||
);
|
||||
|
||||
var dataTypes = (
|
||||
"int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|" +
|
||||
"money|real|number|integer"
|
||||
);
|
||||
|
||||
var keywordMapper = this.createKeywordMapper({
|
||||
"support.function": builtinFunctions,
|
||||
"keyword": keywords,
|
||||
"constant.language": builtinConstants,
|
||||
"storage.type": dataTypes
|
||||
}, "identifier", true);
|
||||
|
||||
this.$rules = {
|
||||
"start" : [ {
|
||||
token : "comment",
|
||||
regex : "--.*$"
|
||||
}, {
|
||||
token : "comment",
|
||||
start : "/\\*",
|
||||
end : "\\*/"
|
||||
}, {
|
||||
token : "string", // " string
|
||||
regex : '".*?"'
|
||||
}, {
|
||||
token : "string", // ' string
|
||||
regex : "'.*?'"
|
||||
}, {
|
||||
token : "string", // ` string (apache drill)
|
||||
regex : "`.*?`"
|
||||
}, {
|
||||
token : "constant.numeric", // float
|
||||
regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
|
||||
}, {
|
||||
token : keywordMapper,
|
||||
regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
|
||||
}, {
|
||||
token : "keyword.operator",
|
||||
regex : "\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="
|
||||
}, {
|
||||
token : "paren.lparen",
|
||||
regex : "[\\(]"
|
||||
}, {
|
||||
token : "paren.rparen",
|
||||
regex : "[\\)]"
|
||||
}, {
|
||||
token : "text",
|
||||
regex : "\\s+"
|
||||
} ]
|
||||
};
|
||||
this.normalizeRules();
|
||||
};
|
||||
|
||||
oop.inherits(SqlHighlightRules, TextHighlightRules);
|
||||
|
||||
exports.SqlHighlightRules = SqlHighlightRules;
|
||||
});
|
||||
|
||||
ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"], function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var TextMode = require("./text").Mode;
|
||||
var SqlHighlightRules = require("./sql_highlight_rules").SqlHighlightRules;
|
||||
|
||||
var Mode = function() {
|
||||
this.HighlightRules = SqlHighlightRules;
|
||||
this.$behaviour = this.$defaultBehaviour;
|
||||
};
|
||||
oop.inherits(Mode, TextMode);
|
||||
|
||||
(function() {
|
||||
|
||||
this.lineCommentStart = "--";
|
||||
|
||||
this.$id = "ace/mode/sql";
|
||||
}).call(Mode.prototype);
|
||||
|
||||
exports.Mode = Mode;
|
||||
|
||||
});
|
@ -1,10 +1,9 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
import {PromCompleter} from './completer';
|
||||
import './mode-prometheus';
|
||||
import './snippets/prometheus';
|
||||
|
||||
class PrometheusQueryCtrl extends QueryCtrl {
|
||||
static templateUrl = 'partials/query.editor.html';
|
||||
@ -44,12 +43,6 @@ class PrometheusQueryCtrl extends QueryCtrl {
|
||||
|
||||
getCompleter(query) {
|
||||
return new PromCompleter(this.datasource);
|
||||
// console.log('getquery);
|
||||
// return this.datasource.performSuggestQuery(query).then(res => {
|
||||
// return res.map(item => {
|
||||
// return {word: item, type: 'metric'};
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
getDefaultFormat() {
|
||||
|
@ -1,13 +1,13 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import 'jquery.flot';
|
||||
import 'jquery.flot.selection';
|
||||
import 'jquery.flot.time';
|
||||
import 'jquery.flot.stack';
|
||||
import 'jquery.flot.stackpercent';
|
||||
import 'jquery.flot.fillbelow';
|
||||
import 'jquery.flot.crosshair';
|
||||
import 'jquery.flot.dashes';
|
||||
import 'vendor/flot/jquery.flot';
|
||||
import 'vendor/flot/jquery.flot.selection';
|
||||
import 'vendor/flot/jquery.flot.time';
|
||||
import 'vendor/flot/jquery.flot.stack';
|
||||
import 'vendor/flot/jquery.flot.stackpercent';
|
||||
import 'vendor/flot/jquery.flot.fillbelow';
|
||||
import 'vendor/flot/jquery.flot.crosshair';
|
||||
import 'vendor/flot/jquery.flot.dashes';
|
||||
import './jquery.flot.events';
|
||||
|
||||
import $ from 'jquery';
|
||||
@ -21,7 +21,8 @@ import {ThresholdManager} from './threshold_manager';
|
||||
import {EventManager} from 'app/features/annotations/all';
|
||||
import {convertValuesToHistogram, getSeriesValues} from './histogram';
|
||||
|
||||
coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
|
||||
/** @ngInject **/
|
||||
function graphDirective($rootScope, timeSrv, popoverSrv) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
template: '',
|
||||
@ -683,4 +684,6 @@ coreModule.directive('grafanaGraph', function($rootScope, timeSrv, popoverSrv) {
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
coreModule.directive('grafanaGraph', graphDirective);
|
||||
|
@ -2,8 +2,6 @@ define([
|
||||
'angular',
|
||||
'lodash',
|
||||
'jquery',
|
||||
'jquery.flot',
|
||||
'jquery.flot.time',
|
||||
],
|
||||
function (angular, _, $) {
|
||||
'use strict';
|
||||
|
@ -0,0 +1,52 @@
|
||||
import {describe, beforeEach, it, expect, sinon, angularMocks} from 'test/lib/common';
|
||||
import '../series_overrides_ctrl';
|
||||
import helpers from 'test/specs/helpers';
|
||||
|
||||
describe('SeriesOverridesCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
var popoverSrv = {};
|
||||
|
||||
beforeEach(angularMocks.module('grafana.services'));
|
||||
beforeEach(angularMocks.module('grafana.controllers'));
|
||||
|
||||
beforeEach(ctx.providePhase({
|
||||
popoverSrv: popoverSrv
|
||||
}));
|
||||
|
||||
beforeEach(angularMocks.inject(function($rootScope, $controller) {
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.scope.ctrl = {
|
||||
refresh: sinon.spy(),
|
||||
render: sinon.spy(),
|
||||
seriesList: []
|
||||
};
|
||||
ctx.scope.render = function() {};
|
||||
ctx.controller = $controller('SeriesOverridesCtrl', {
|
||||
$scope: ctx.scope
|
||||
});
|
||||
}));
|
||||
|
||||
describe('When setting an override', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.setOverride({propertyName: 'lines'}, {value: true});
|
||||
});
|
||||
|
||||
it('should set override property', function() {
|
||||
expect(ctx.scope.override.lines).to.be(true);
|
||||
});
|
||||
|
||||
it('should update view model', function() {
|
||||
expect(ctx.scope.currentOverrides[0].name).to.be('Lines');
|
||||
expect(ctx.scope.currentOverrides[0].value).to.be('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When removing overide', function() {
|
||||
it('click should include option and value index', function() {
|
||||
ctx.scope.setOverride(1,0);
|
||||
ctx.scope.removeOverride({ propertyName: 'lines' });
|
||||
expect(ctx.scope.currentOverrides.length).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,4 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import 'jquery.flot';
|
||||
import 'vendor/flot/jquery.flot';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import d3 from 'd3';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import {contextSrv} from 'app/core/core';
|
||||
import {tickStep} from 'app/core/utils/ticks';
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import d3 from 'd3';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import $ from 'jquery';
|
||||
import _ from 'lodash';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
|
@ -1,4 +1,3 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
import './color_legend';
|
||||
import {HeatmapCtrl} from './heatmap_ctrl';
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import moment from 'moment';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import {appEvents, contextSrv} from 'app/core/core';
|
||||
import {tickStep, getScaledDecimals, getFlotTickSize} from 'app/core/utils/ticks';
|
||||
import d3 from 'd3';
|
||||
import d3 from 'vendor/d3/d3';
|
||||
import {HeatmapTooltip} from './heatmap_tooltip';
|
||||
import {mergeZeroBuckets} from './heatmap_data_converter';
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import 'jquery.flot';
|
||||
import 'jquery.flot.gauge';
|
||||
import 'vendor/flot/jquery.flot';
|
||||
import 'vendor/flot/jquery.flot.gauge';
|
||||
import 'app/features/panellinks/linkSrv';
|
||||
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import config from 'app/core/config';
|
||||
|
@ -1,8 +1,6 @@
|
||||
///<reference path="../../../../headers/common.d.ts" />
|
||||
import {describe, beforeEach, afterEach, it, sinon, expect, angularMocks} from 'test/lib/common';
|
||||
|
||||
import {describe, beforeEach, afterEach, it, sinon, expect, angularMocks} from '../../../../../test/lib/common';
|
||||
|
||||
import helpers from '../../../../../test/specs/helpers';
|
||||
import helpers from 'test/specs/helpers';
|
||||
import {SingleStatCtrl} from '../module';
|
||||
import moment from 'moment';
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import {PanelCtrl} from 'app/plugins/sdk';
|
||||
import {PanelCtrl} from 'app/features/panel/panel_ctrl';
|
||||
|
||||
export class UnknownPanelCtrl extends PanelCtrl {
|
||||
static templateUrl = 'public/app/plugins/panel/unknown/module.html';
|
||||
@ -9,7 +8,6 @@ export class UnknownPanelCtrl extends PanelCtrl {
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,59 +0,0 @@
|
||||
# Plugin API
|
||||
|
||||
### 3.0 changes to plugin api changes
|
||||
|
||||
There has been big changes to both data source and plugin schema (plugin.json) and how
|
||||
you write the plugin main module.
|
||||
|
||||
#### Datasource plugin
|
||||
|
||||
Now data source plugins AMD/SystemJS module should return:
|
||||
|
||||
```javascript
|
||||
return {
|
||||
Datasource: ElasticDatasource,
|
||||
configView: editView.default,
|
||||
annotationsQueryEditor: annotationsQueryEditor,
|
||||
metricsQueryEditor: metricsQueryEditor,
|
||||
metricsQueryOptions: metricsQueryOptions,
|
||||
};
|
||||
```
|
||||
|
||||
Where ElasticDatasource is a constructor function to a javascript. The constructor
|
||||
function can take angular services and `instanceSettings` as parameters.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
function ElasticDatasource(instanceSettings, templateSrv) {
|
||||
this.instanceSettings = this.instanceSettings;
|
||||
///...
|
||||
};
|
||||
```
|
||||
|
||||
A datasource module can optionally return a configView directive function, metricsQueryEditor directive function, etc.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
function metricsQueryEditor() {
|
||||
return {controller: 'ElasticQueryCtrl', templateUrl: 'public/app/plugins/datasource/elasticsearch/partials/query.editor.html'};
|
||||
}
|
||||
```
|
||||
|
||||
#### Panel plugin
|
||||
|
||||
The panel plugin AMD/SystemJS module should return an object with a property named `panel`. This needs to be
|
||||
a directive function.
|
||||
|
||||
### 2.5.1 changes
|
||||
datasource annotationQuery changed. now single options parameter with:
|
||||
- range
|
||||
- rangeRaw
|
||||
- annotation
|
||||
|
||||
2.5 changed the `range` parameter in the `datasource.query` function's options parameter. This
|
||||
parameter now holds a parsed range with `moment` dates `form` and `to`. To get
|
||||
millisecond epoch from a `moment` you the function `valueOf`. The raw date range as represented
|
||||
internally in grafana (which may be relative expressions like `now-5h`) is included in the
|
||||
new property `rangeRaw` (on the options object).
|
@ -2,20 +2,12 @@ import {PanelCtrl} from 'app/features/panel/panel_ctrl';
|
||||
import {MetricsPanelCtrl} from 'app/features/panel/metrics_panel_ctrl';
|
||||
import {QueryCtrl} from 'app/features/panel/query_ctrl';
|
||||
import {alertTab} from 'app/features/alerting/alert_tab_ctrl';
|
||||
|
||||
import config from 'app/core/config';
|
||||
|
||||
export function loadPluginCss(options) {
|
||||
if (config.bootData.user.lightTheme) {
|
||||
System.import(options.light + '!css');
|
||||
} else {
|
||||
System.import(options.dark + '!css');
|
||||
}
|
||||
}
|
||||
import {loadPluginCss} from 'app/features/plugins/plugin_loader';
|
||||
|
||||
export {
|
||||
PanelCtrl,
|
||||
MetricsPanelCtrl,
|
||||
QueryCtrl,
|
||||
alertTab,
|
||||
loadPluginCss,
|
||||
};
|
||||
|
@ -1,89 +0,0 @@
|
||||
System.config({
|
||||
warnings: true,
|
||||
defaultExtenion: 'js',
|
||||
baseURL: 'public',
|
||||
paths: {
|
||||
'react': 'vendor/npm/react/dist/react.js',
|
||||
'react-dom': 'vendor/npm/react-dom/dist/react-dom.js',
|
||||
'ngreact': 'vendor/npm/ngreact/ngReact.js',
|
||||
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
|
||||
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
|
||||
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
||||
'moment': 'vendor/moment.js',
|
||||
"jquery": "vendor/npm/jquery/dist/jquery.js",
|
||||
'lodash-src': 'vendor/npm/lodash/lodash.js',
|
||||
"lodash": 'app/core/lodash_extended.js',
|
||||
"angular": "vendor/npm/angular/angular.js",
|
||||
"bootstrap": "vendor/bootstrap/bootstrap.js",
|
||||
'angular-route': 'vendor/npm/angular-route/angular-route.js',
|
||||
'angular-sanitize': 'vendor/npm/angular-sanitize/angular-sanitize.js',
|
||||
"angular-ui": "vendor/angular-ui/ui-bootstrap-tpls.js",
|
||||
"angular-strap": "vendor/angular-other/angular-strap.js",
|
||||
"angular-dragdrop": "vendor/npm/angular-native-dragdrop/draganddrop.js",
|
||||
"angular-bindonce": "vendor/npm/angular-bindonce/bindonce.js",
|
||||
"spectrum": "vendor/spectrum.js",
|
||||
"bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
|
||||
"jquery.flot": "vendor/flot/jquery.flot",
|
||||
"jquery.flot.pie": "vendor/flot/jquery.flot.pie",
|
||||
"jquery.flot.selection": "vendor/flot/jquery.flot.selection",
|
||||
"jquery.flot.stack": "vendor/flot/jquery.flot.stack",
|
||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
|
||||
"d3": "vendor/d3/d3.js",
|
||||
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
|
||||
"ace": "vendor/npm/ace-builds/src-noconflict/ace",
|
||||
"clipboard": "vendor/npm/clipboard/dist/clipboard.js"
|
||||
},
|
||||
|
||||
packages: {
|
||||
app: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
vendor: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
plugins: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
test: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
},
|
||||
|
||||
map: {
|
||||
text: 'vendor/plugin-text/text.js',
|
||||
css: 'app/core/utils/css_loader.js'
|
||||
},
|
||||
|
||||
meta: {
|
||||
'vendor/angular-other/angular-strap.js': {
|
||||
deps: ['angular']
|
||||
},
|
||||
'vendor/npm/virtual-scroll/src/indx.js': {
|
||||
format: 'cjs',
|
||||
exports: 'VirtualScroll',
|
||||
},
|
||||
'vendor/npm/angular/angular.js': {
|
||||
format: 'global',
|
||||
deps: ['jquery'],
|
||||
exports: 'angular',
|
||||
},
|
||||
'vendor/npm/eventemitter3/index.js': {
|
||||
format: 'cjs',
|
||||
exports: 'EventEmitter'
|
||||
},
|
||||
'vendor/npm/mousetrap/mousetrap.js': {
|
||||
format: 'global',
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'vendor/npm/ace-builds/src-noconflict/ace.js': {
|
||||
format: 'global',
|
||||
exports: 'ace'
|
||||
}
|
||||
}
|
||||
});
|
@ -1,3 +1,7 @@
|
||||
// vendor
|
||||
@import "../vendor/css/timepicker.css";
|
||||
@import "../vendor/css/spectrum.css";
|
||||
|
||||
// MIXINS
|
||||
@import "mixins/mixins";
|
||||
@import "mixins/animations";
|
||||
@ -15,8 +19,7 @@
|
||||
@import "base/type";
|
||||
@import "base/forms";
|
||||
@import "base/grid";
|
||||
@import "base/font_awesome";
|
||||
@import "base/grafana_icons";
|
||||
@import "base/fonts";
|
||||
@import "base/code";
|
||||
|
||||
// UTILS
|
||||
|
@ -1,24 +1,5 @@
|
||||
/* icon fonts
|
||||
|
||||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('../fonts/fontawesome-webfont.eot?v=4.2.0');
|
||||
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'grafana-icons';
|
||||
src: url('../fonts/grafana-icons.eot?h6rv8b');
|
||||
src: url('../fonts/grafana-icons.eot?h6rv8b#iefix') format('embedded-opentype'),
|
||||
url('../fonts/grafana-icons.ttf?h6rv8b') format('truetype'),
|
||||
url('../fonts/grafana-icons.woff?h6rv8b') format('woff'),
|
||||
url('../fonts/grafana-icons.svg?h6rv8b#grafana-icons') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}*/
|
||||
|
||||
@import "base/font_awesome";
|
||||
@import "base/grafana_icons";
|
||||
|
||||
/* Open sans fonts*/
|
||||
|
||||
@ -301,4 +282,4 @@
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(../fonts/opensans/xjAJXh38I15wypJXxuGMBo4P5ICox8Kq3LLUNMylGO4.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
min-width: 20rem;
|
||||
flex-grow: 1;
|
||||
margin-right: 0.25rem;
|
||||
visibility: hidden;
|
||||
|
||||
&.ace_editor {
|
||||
@include font-family-monospace();
|
||||
@ -13,10 +12,6 @@
|
||||
@include border-radius($input-border-radius-sm);
|
||||
border: $input-btn-border-width solid $input-border-color;
|
||||
}
|
||||
|
||||
&--theme-loaded {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.ace_editor.ace_autocomplete {
|
||||
|
@ -3,14 +3,7 @@
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
.query-segment-key {
|
||||
//border-right: none;
|
||||
//padding-right: 1px;
|
||||
}
|
||||
|
||||
.query-segment-operator {
|
||||
//padding-right: 1px;
|
||||
//border-right: none;
|
||||
color: $orange;
|
||||
}
|
||||
|
||||
|
@ -26,52 +26,3 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// define("areas/styleguide/static/script/app/colors", [], function() {
|
||||
// "use strict";
|
||||
// var a = function(a) {
|
||||
// return 0 === a.indexOf("rgba") ? (a = a.match(/(\d{1,3})/gi),
|
||||
// [a[0], a[1], a[2]]) : (a = a.replace(/^#/, "").match(/[0-9a-z]{2}/gi),
|
||||
// [parseInt(a[0], 16), parseInt(a[1], 16), parseInt(a[2], 16)])
|
||||
// }
|
||||
// , b = function(b, c) {
|
||||
// var d = 0
|
||||
// , e = 0;
|
||||
// for (b = a(b),
|
||||
// c = a(c); e < b.length; )
|
||||
// d += (b[e] - c[e]) * (b[e] - c[e]),
|
||||
// e++;
|
||||
// return Math.sqrt(d)
|
||||
// }
|
||||
// , c = function() {
|
||||
// $(".color-card").removeClass("isnt-approximate is-approximate is-exact")
|
||||
// }
|
||||
// , d = function() {
|
||||
// var a = $("#proximityMatch").val()
|
||||
// , c = $(".swatch");
|
||||
// c.each(function() {
|
||||
// var c = $(this)
|
||||
// , d = b(a, c.children("i").text());
|
||||
// c.parent().removeClass("is-approximate isnt-approximate is-exact"),
|
||||
// 20 > d ? (c.css("border-color", /^#/.test(a) ? a : "#" + a),
|
||||
// c.parent().addClass("is-approximate"),
|
||||
// c.parent().addClass("has-match"),
|
||||
// 0 === d && c.parent().addClass("is-exact")) : c.parent().addClass("isnt-approximate")
|
||||
// })
|
||||
// }
|
||||
// , e = $("#proximityMatch")
|
||||
// , f = function() {
|
||||
// var a = e.val();
|
||||
// "#" == a[0] && (a = a.substring(1)),
|
||||
// 6 == a.length ? d() : c()
|
||||
// }
|
||||
// ;
|
||||
// e.on("input", f),
|
||||
// e.val() && f();
|
||||
// var g = function() {
|
||||
// var a = "/static/images/styleguide/leaves.jpg"
|
||||
// , b = $("[data-colors]");
|
||||
// "none" === b.css("background-image") ? b.css("background-image", "url('" + a + "')") : b.css("background-image", "none")
|
||||
// }
|
||||
// ;
|
||||
// $("#toggle-background-image").on("click", g)
|
||||
// })
|
||||
|
31
public/test/index.ts
Normal file
31
public/test/index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// const context = require.context('./', true, /_specs\.ts/);
|
||||
// context.keys().forEach(context);
|
||||
// module.exports = context;
|
||||
|
||||
import 'babel-polyfill';
|
||||
import 'jquery';
|
||||
import angular from 'angular';
|
||||
import 'angular-mocks';
|
||||
import 'app/app';
|
||||
|
||||
// configure enzyme
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
|
||||
angular.module('grafana', ['ngRoute']);
|
||||
angular.module('grafana.services', ['ngRoute', '$strap.directives']);
|
||||
angular.module('grafana.panels', []);
|
||||
angular.module('grafana.controllers', []);
|
||||
angular.module('grafana.directives', []);
|
||||
angular.module('grafana.filters', []);
|
||||
angular.module('grafana.routes', ['ngRoute']);
|
||||
|
||||
const context = (<any>require).context('../', true, /specs/);
|
||||
for (let key of context.keys()) {
|
||||
context(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
///<reference path="../../app/headers/common.d.ts" />
|
||||
|
||||
var _global = <any>(window);
|
||||
var beforeEach = _global.beforeEach;
|
||||
var afterEach = _global.afterEach;
|
||||
|
@ -1,33 +0,0 @@
|
||||
define([
|
||||
'app/core/config',
|
||||
'app/core/services/backend_srv'
|
||||
], function() {
|
||||
'use strict';
|
||||
|
||||
describe('backend_srv', function() {
|
||||
var _backendSrv;
|
||||
var _http;
|
||||
var _httpBackend;
|
||||
|
||||
beforeEach(module('grafana.core'));
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(inject(function ($httpBackend, $http, backendSrv) {
|
||||
_httpBackend = $httpBackend;
|
||||
_http = $http;
|
||||
_backendSrv = backendSrv;
|
||||
}));
|
||||
|
||||
describe('when handling errors', function() {
|
||||
it('should return the http status code', function(done) {
|
||||
_httpBackend.whenGET('gateway-error').respond(502);
|
||||
_backendSrv.datasourceRequest({
|
||||
url: 'gateway-error'
|
||||
}).catch(function(err) {
|
||||
expect(err.status).to.be(502);
|
||||
done();
|
||||
});
|
||||
_httpBackend.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,56 +0,0 @@
|
||||
define([
|
||||
'app/features/dashboard/viewStateSrv',
|
||||
'app/core/config'
|
||||
], function(viewStateSrv, config) {
|
||||
'use strict';
|
||||
|
||||
describe('when updating view state', function() {
|
||||
var viewState, location;
|
||||
var timeSrv = {};
|
||||
var templateSrv = {};
|
||||
var contextSrv = {
|
||||
user: {
|
||||
orgId: 19
|
||||
}
|
||||
};
|
||||
beforeEach(function() {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('timeSrv', timeSrv);
|
||||
$provide.value('templateSrv', templateSrv);
|
||||
$provide.value('contextSrv', contextSrv);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function(dashboardViewStateSrv, $location, $rootScope) {
|
||||
$rootScope.onAppEvent = function() {};
|
||||
$rootScope.dashboard = {meta: {}};
|
||||
viewState = dashboardViewStateSrv.create($rootScope);
|
||||
location = $location;
|
||||
}));
|
||||
|
||||
describe('to fullscreen true and edit true', function() {
|
||||
it('should update querystring and view state', function() {
|
||||
var updateState = {fullscreen: true, edit: true, panelId: 1};
|
||||
viewState.update(updateState);
|
||||
expect(location.search()).to.eql({fullscreen: true, edit: true, panelId: 1, orgId: 1});
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(true);
|
||||
expect(viewState.state.fullscreen).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('to fullscreen false', function() {
|
||||
it('should remove params from query string', function() {
|
||||
viewState.update({fullscreen: true, panelId: 1, edit: true});
|
||||
viewState.update({fullscreen: false});
|
||||
expect(viewState.dashboard.meta.fullscreen).to.be(false);
|
||||
expect(viewState.state.fullscreen).to.be(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,63 +0,0 @@
|
||||
define([
|
||||
'app/core/config',
|
||||
'app/core/services/datasource_srv'
|
||||
], function(config) {
|
||||
'use strict';
|
||||
|
||||
describe('datasource_srv', function() {
|
||||
var _datasourceSrv;
|
||||
var metricSources;
|
||||
var templateSrv = {};
|
||||
|
||||
beforeEach(module('grafana.core'));
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('templateSrv', templateSrv);
|
||||
}));
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(inject(function(datasourceSrv) {
|
||||
_datasourceSrv = datasourceSrv;
|
||||
}));
|
||||
|
||||
describe('when loading metric sources', function() {
|
||||
var unsortedDatasources = {
|
||||
'mmm': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
'--Grafana--': {
|
||||
type: 'grafana',
|
||||
meta: {builtIn: true, metrics: {m: 1}, id: "grafana"}
|
||||
},
|
||||
'--Mixed--': {
|
||||
type: 'test-db',
|
||||
meta: {builtIn: true, metrics: {m: 1}, id: "mixed"}
|
||||
},
|
||||
'ZZZ': {
|
||||
type: 'test-db',
|
||||
meta: {metrics: {m: 1} }
|
||||
},
|
||||
'aaa': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
'BBB': {
|
||||
type: 'test-db',
|
||||
meta: { metrics: {m: 1} }
|
||||
},
|
||||
};
|
||||
beforeEach(function() {
|
||||
config.datasources = unsortedDatasources;
|
||||
metricSources = _datasourceSrv.getMetricSources({skipVariables: true});
|
||||
});
|
||||
|
||||
it('should return a list of sources sorted case insensitively with builtin sources last', function() {
|
||||
expect(metricSources[0].name).to.be('aaa');
|
||||
expect(metricSources[1].name).to.be('BBB');
|
||||
expect(metricSources[2].name).to.be('mmm');
|
||||
expect(metricSources[3].name).to.be('ZZZ');
|
||||
expect(metricSources[4].name).to.be('--Grafana--');
|
||||
expect(metricSources[5].name).to.be('--Mixed--');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -25,7 +25,7 @@ define([
|
||||
};
|
||||
|
||||
this.providePhase = function(mocks) {
|
||||
return module(function($provide) {
|
||||
return window.module(function($provide) {
|
||||
$provide.value('datasourceSrv', self.datasourceSrv);
|
||||
$provide.value('annotationsSrv', self.annotationsSrv);
|
||||
$provide.value('timeSrv', self.timeSrv);
|
||||
@ -38,7 +38,7 @@ define([
|
||||
};
|
||||
|
||||
this.createPanelController = function(Ctrl) {
|
||||
return inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
return window.inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
self.scope = $rootScope.$new();
|
||||
self.$location = $location;
|
||||
self.$browser = $browser;
|
||||
@ -60,7 +60,7 @@ define([
|
||||
};
|
||||
|
||||
this.createControllerPhase = function(controllerName) {
|
||||
return inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
return window.inject(function($controller, $rootScope, $q, $location, $browser) {
|
||||
self.scope = $rootScope.$new();
|
||||
self.$location = $location;
|
||||
self.$browser = $browser;
|
||||
@ -95,7 +95,7 @@ define([
|
||||
self.$routeParams = {};
|
||||
|
||||
this.providePhase = function(mocks) {
|
||||
return module(function($provide) {
|
||||
return window.module(function($provide) {
|
||||
_.each(mocks, function(key) {
|
||||
$provide.value(key, self[key]);
|
||||
});
|
||||
@ -103,7 +103,7 @@ define([
|
||||
};
|
||||
|
||||
this.createService = function(name) {
|
||||
return inject(function($q, $rootScope, $httpBackend, $injector, $location) {
|
||||
return window.inject(function($q, $rootScope, $httpBackend, $injector, $location) {
|
||||
self.$q = $q;
|
||||
self.$rootScope = $rootScope;
|
||||
self.$httpBackend = $httpBackend;
|
||||
|
@ -1,52 +0,0 @@
|
||||
define([
|
||||
'lodash',
|
||||
'app/features/dashboard/all',
|
||||
'app/features/panellinks/linkSrv'
|
||||
], function(_) {
|
||||
'use strict';
|
||||
|
||||
describe('linkSrv', function() {
|
||||
var _linkSrv;
|
||||
|
||||
beforeEach(module('grafana.core'));
|
||||
beforeEach(module('grafana.services'));
|
||||
|
||||
beforeEach(inject(function(linkSrv) {
|
||||
_linkSrv = linkSrv;
|
||||
}));
|
||||
|
||||
describe('when appending query strings', function() {
|
||||
|
||||
it('add ? to URL if not present', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com', 'foo=bar');
|
||||
expect(url).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
|
||||
it('do not add & to URL if ? is present but query string is empty', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com?', 'foo=bar');
|
||||
expect(url).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
|
||||
it('add & to URL if query string is present', function() {
|
||||
var url = _linkSrv.appendToQueryString('http://example.com?foo=bar', 'hello=world');
|
||||
expect(url).to.be('http://example.com?foo=bar&hello=world');
|
||||
});
|
||||
|
||||
it('do not change the URL if there is nothing to append', function() {
|
||||
_.each(['', undefined, null], function(toAppend) {
|
||||
var url1 = _linkSrv.appendToQueryString('http://example.com', toAppend);
|
||||
expect(url1).to.be('http://example.com');
|
||||
|
||||
var url2 = _linkSrv.appendToQueryString('http://example.com?', toAppend);
|
||||
expect(url2).to.be('http://example.com?');
|
||||
|
||||
var url3 = _linkSrv.appendToQueryString('http://example.com?foo=bar', toAppend);
|
||||
expect(url3).to.be('http://example.com?foo=bar');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -1,59 +0,0 @@
|
||||
define([
|
||||
'./helpers',
|
||||
'app/plugins/panel/graph/series_overrides_ctrl'
|
||||
], function(helpers) {
|
||||
'use strict';
|
||||
|
||||
describe('SeriesOverridesCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
var popoverSrv = {};
|
||||
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(module('grafana.controllers'));
|
||||
|
||||
beforeEach(ctx.providePhase({
|
||||
popoverSrv: popoverSrv
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($rootScope, $controller) {
|
||||
// ctx.createControllerPhase('SeriesOverridesCtrl'));
|
||||
// beforeEach(function() {
|
||||
ctx.scope = $rootScope.$new();
|
||||
ctx.scope.ctrl = {
|
||||
refresh: sinon.spy(),
|
||||
render: sinon.spy(),
|
||||
seriesList: []
|
||||
};
|
||||
ctx.scope.render = function() {};
|
||||
ctx.controller = $controller('SeriesOverridesCtrl', {
|
||||
$scope: ctx.scope
|
||||
});
|
||||
}));
|
||||
|
||||
describe('When setting an override', function() {
|
||||
beforeEach(function() {
|
||||
ctx.scope.setOverride({propertyName: 'lines'}, {value: true});
|
||||
});
|
||||
|
||||
it('should set override property', function() {
|
||||
expect(ctx.scope.override.lines).to.be(true);
|
||||
});
|
||||
|
||||
it('should update view model', function() {
|
||||
expect(ctx.scope.currentOverrides[0].name).to.be('Lines');
|
||||
expect(ctx.scope.currentOverrides[0].value).to.be('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When removing overide', function() {
|
||||
it('click should include option and value index', function() {
|
||||
ctx.scope.setOverride(1,0);
|
||||
ctx.scope.removeOverride({ propertyName: 'lines' });
|
||||
expect(ctx.scope.currentOverrides.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,115 +0,0 @@
|
||||
define([
|
||||
'./helpers',
|
||||
'app/features/dashboard/shareModalCtrl',
|
||||
'app/features/panellinks/linkSrv',
|
||||
'app/core/config',
|
||||
], function(helpers, shareModalCtrl, linkSrv, config) {
|
||||
'use strict';
|
||||
|
||||
describe('ShareModalCtrl', function() {
|
||||
var ctx = new helpers.ControllerTestContext();
|
||||
|
||||
function setTime(range) {
|
||||
ctx.timeSrv.timeRange = sinon.stub().returns(range);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
config.bootData = {
|
||||
user: {
|
||||
orgId: 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
setTime({ from: new Date(1000), to: new Date(2000) });
|
||||
|
||||
beforeEach(module('grafana.controllers'));
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(module(function($compileProvider) {
|
||||
$compileProvider.preAssignBindingsEnabled(true);
|
||||
}));
|
||||
|
||||
beforeEach(ctx.providePhase());
|
||||
|
||||
beforeEach(ctx.createControllerPhase('ShareModalCtrl'));
|
||||
|
||||
describe('shareUrl with current time range and panel', function() {
|
||||
it('should generate share url absolute time', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&panelId=22&fullscreen');
|
||||
});
|
||||
|
||||
it('should generate render url', function() {
|
||||
ctx.$location.$$absUrl = 'http://dashboards.grafana.com/dashboard/db/my-dash';
|
||||
|
||||
ctx.scope.panel = { id: 22 };
|
||||
|
||||
ctx.scope.init();
|
||||
var base = 'http://dashboards.grafana.com/render/dashboard-solo/db/my-dash';
|
||||
var params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||
expect(ctx.scope.imageUrl).to.contain(base + params);
|
||||
});
|
||||
|
||||
it('should remove panel id when no panel in scope', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.forCurrent = true;
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1');
|
||||
});
|
||||
|
||||
it('should add theme when specified', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.theme = 'light';
|
||||
ctx.scope.panel = null;
|
||||
|
||||
ctx.scope.init();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&theme=light');
|
||||
});
|
||||
|
||||
it('should remove fullscreen from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?fullscreen&edit');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?fullscreen&edit&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
|
||||
});
|
||||
|
||||
it('should remove edit from image url when is first param in querystring and modeSharePanel is true', function() {
|
||||
ctx.$location.url('/test?edit&fullscreen');
|
||||
ctx.scope.modeSharePanel = true;
|
||||
ctx.scope.panel = { id: 1 };
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
|
||||
expect(ctx.scope.shareUrl).to.contain('?edit&fullscreen&from=1000&to=2000&orgId=1&panelId=1');
|
||||
expect(ctx.scope.imageUrl).to.contain('?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
|
||||
|
||||
});
|
||||
|
||||
it('should include template variables in url', function() {
|
||||
ctx.$location.path('/test');
|
||||
ctx.scope.options.includeTemplateVars = true;
|
||||
|
||||
ctx.templateSrv.fillVariableValuesForUrl = function(params) {
|
||||
params['var-app'] = 'mupp';
|
||||
params['var-server'] = 'srv-01';
|
||||
};
|
||||
|
||||
ctx.scope.buildUrl();
|
||||
expect(ctx.scope.shareUrl).to.be('http://server/#!/test?from=1000&to=2000&orgId=1&var-app=mupp&var-server=srv-01');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,86 +0,0 @@
|
||||
define([
|
||||
'app/features/dashboard/unsavedChangesSrv',
|
||||
'app/features/dashboard/dashboard_srv'
|
||||
], function() {
|
||||
'use strict';
|
||||
|
||||
describe("unsavedChangesSrv", function() {
|
||||
var _unsavedChangesSrv;
|
||||
var _dashboardSrv;
|
||||
var _location;
|
||||
var _contextSrvStub = { isEditor: true };
|
||||
var _rootScope;
|
||||
var tracker;
|
||||
var dash;
|
||||
var scope;
|
||||
|
||||
beforeEach(module('grafana.core'));
|
||||
beforeEach(module('grafana.services'));
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('contextSrv', _contextSrvStub);
|
||||
$provide.value('$window', {});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function(unsavedChangesSrv, $location, $rootScope, dashboardSrv) {
|
||||
_unsavedChangesSrv = unsavedChangesSrv;
|
||||
_dashboardSrv = dashboardSrv;
|
||||
_location = $location;
|
||||
_rootScope = $rootScope;
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
dash = _dashboardSrv.create({
|
||||
refresh: false,
|
||||
rows: [
|
||||
{
|
||||
panels: [{ test: "asd", legend: { } }]
|
||||
}
|
||||
]
|
||||
});
|
||||
scope = _rootScope.$new();
|
||||
scope.appEvent = sinon.spy();
|
||||
scope.onAppEvent = sinon.spy();
|
||||
|
||||
tracker = new _unsavedChangesSrv.Tracker(dash, scope);
|
||||
});
|
||||
|
||||
it('No changes should not have changes', function() {
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Simple change should be registered', function() {
|
||||
dash.property = "google";
|
||||
expect(tracker.hasChanges()).to.be(true);
|
||||
});
|
||||
|
||||
it('Should ignore a lot of changes', function() {
|
||||
dash.time = {from: '1h'};
|
||||
dash.refresh = true;
|
||||
dash.schemaVersion = 10;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore row collapse change', function() {
|
||||
dash.rows[0].collapse = true;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore panel legend changes', function() {
|
||||
dash.rows[0].panels[0].legend.sortDesc = true;
|
||||
dash.rows[0].panels[0].legend.sort = "avg";
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore panel repeats', function() {
|
||||
dash.rows[0].panels.push({repeatPanelId: 10});
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
it('Should ignore row repeats', function() {
|
||||
dash.addEmptyRow();
|
||||
dash.rows[1].repeatRowId = 10;
|
||||
expect(tracker.hasChanges()).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -1,171 +0,0 @@
|
||||
define([
|
||||
'app/core/directives/value_select_dropdown',
|
||||
],
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
describe("SelectDropdownCtrl", function() {
|
||||
var scope;
|
||||
var ctrl;
|
||||
var tagValuesMap = {};
|
||||
var rootScope;
|
||||
var q;
|
||||
|
||||
beforeEach(module('grafana.core'));
|
||||
beforeEach(inject(function($controller, $rootScope, $q, $httpBackend) {
|
||||
rootScope = $rootScope;
|
||||
q = $q;
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('ValueSelectDropdownCtrl', {$scope: scope});
|
||||
ctrl.onUpdated = sinon.spy();
|
||||
$httpBackend.when('GET', /\.html$/).respond('');
|
||||
}));
|
||||
|
||||
describe("Given simple variable", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'hej', value: 'hej' },
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
};
|
||||
ctrl.init();
|
||||
});
|
||||
|
||||
it("Should init labelText and linkText", function() {
|
||||
expect(ctrl.linkText).to.be("hej");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given variable with tags and dropdown is opened", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'server-1', value: 'server-1'},
|
||||
options: [
|
||||
{text: 'server-1', value: 'server-1', selected: true},
|
||||
{text: 'server-2', value: 'server-2'},
|
||||
{text: 'server-3', value: 'server-3'},
|
||||
],
|
||||
tags: ["key1", "key2", "key3"],
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
multi: true
|
||||
};
|
||||
tagValuesMap.key1 = ['server-1', 'server-3'];
|
||||
tagValuesMap.key2 = ['server-2', 'server-3'];
|
||||
tagValuesMap.key3 = ['server-1', 'server-2', 'server-3'];
|
||||
ctrl.init();
|
||||
ctrl.show();
|
||||
});
|
||||
|
||||
it("should init tags model", function() {
|
||||
expect(ctrl.tags.length).to.be(3);
|
||||
expect(ctrl.tags[0].text).to.be("key1");
|
||||
});
|
||||
|
||||
it("should init options model", function() {
|
||||
expect(ctrl.options.length).to.be(3);
|
||||
});
|
||||
|
||||
it("should init selected values array", function() {
|
||||
expect(ctrl.selectedValues.length).to.be(1);
|
||||
});
|
||||
|
||||
it("should set linkText", function() {
|
||||
expect(ctrl.linkText).to.be('server-1');
|
||||
});
|
||||
|
||||
describe('after adititional value is selected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectValue(ctrl.options[2], {});
|
||||
ctrl.commitChanges();
|
||||
});
|
||||
|
||||
it('should update link text', function() {
|
||||
expect(ctrl.linkText).to.be('server-1 + server-3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When tag is selected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectTag(ctrl.tags[0]);
|
||||
rootScope.$digest();
|
||||
ctrl.commitChanges();
|
||||
});
|
||||
|
||||
it("should select tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(1);
|
||||
});
|
||||
|
||||
it("should select values", function() {
|
||||
expect(ctrl.options[0].selected).to.be(true);
|
||||
expect(ctrl.options[2].selected).to.be(true);
|
||||
});
|
||||
|
||||
it("link text should not include tag values", function() {
|
||||
expect(ctrl.linkText).to.be('');
|
||||
});
|
||||
|
||||
describe('and then dropdown is opened and closed without changes', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.show();
|
||||
ctrl.commitChanges();
|
||||
rootScope.$digest();
|
||||
});
|
||||
|
||||
it("should still have selected tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and then unselected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectTag(ctrl.tags[0]);
|
||||
rootScope.$digest();
|
||||
});
|
||||
|
||||
it("should deselect tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and then value is unselected', function() {
|
||||
beforeEach(function() {
|
||||
ctrl.selectValue(ctrl.options[0], {});
|
||||
});
|
||||
|
||||
it("should deselect tag", function() {
|
||||
expect(ctrl.selectedTags.length).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Given variable with selected tags", function() {
|
||||
beforeEach(function() {
|
||||
ctrl.variable = {
|
||||
current: {text: 'server-1', value: 'server-1', tags: [{text: 'key1', selected: true}] },
|
||||
options: [
|
||||
{text: 'server-1', value: 'server-1'},
|
||||
{text: 'server-2', value: 'server-2'},
|
||||
{text: 'server-3', value: 'server-3'},
|
||||
],
|
||||
tags: ["key1", "key2", "key3"],
|
||||
getValuesForTag: function(key) {
|
||||
return q.when(tagValuesMap[key]);
|
||||
},
|
||||
multi: true
|
||||
};
|
||||
ctrl.init();
|
||||
ctrl.show();
|
||||
});
|
||||
|
||||
it("should set tag as selected", function() {
|
||||
expect(ctrl.tags[0].selected).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -1,139 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// Tun on full stack traces in errors to help debugging
|
||||
Error.stackTraceLimit=Infinity;
|
||||
|
||||
window.__karma__.loaded = function() {};
|
||||
|
||||
System.config({
|
||||
baseURL: '/base/',
|
||||
defaultExtension: 'js',
|
||||
paths: {
|
||||
'react': 'vendor/npm/react/dist/react.js',
|
||||
'react-dom': 'vendor/npm/react-dom/dist/react-dom.js',
|
||||
'ngreact': 'vendor/npm/ngreact/ngReact.js',
|
||||
'mousetrap': 'vendor/npm/mousetrap/mousetrap.js',
|
||||
'eventemitter3': 'vendor/npm/eventemitter3/index.js',
|
||||
'remarkable': 'vendor/npm/remarkable/dist/remarkable.js',
|
||||
'tether': 'vendor/npm/tether/dist/js/tether.js',
|
||||
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
|
||||
'moment': 'vendor/moment.js',
|
||||
"jquery": "vendor/npm/jquery/dist/jquery.js",
|
||||
'lodash-src': 'vendor/npm/lodash/lodash.js',
|
||||
"lodash": 'app/core/lodash_extended.js',
|
||||
"angular": 'vendor/npm/angular/angular.js',
|
||||
'angular-mocks': 'vendor/npm/angular-mocks/angular-mocks.js',
|
||||
"bootstrap": "vendor/bootstrap/bootstrap.js",
|
||||
'angular-route': 'vendor/npm/angular-route/angular-route.js',
|
||||
'angular-sanitize': 'vendor/npm/angular-sanitize/angular-sanitize.js',
|
||||
"angular-ui": "vendor/angular-ui/ui-bootstrap-tpls.js",
|
||||
"angular-strap": "vendor/angular-other/angular-strap.js",
|
||||
"angular-dragdrop": "vendor/npm/angular-native-dragdrop/draganddrop.js",
|
||||
"angular-bindonce": "vendor/npm/angular-bindonce/bindonce.js",
|
||||
"spectrum": "vendor/spectrum.js",
|
||||
"bootstrap-tagsinput": "vendor/tagsinput/bootstrap-tagsinput.js",
|
||||
"jquery.flot": "vendor/flot/jquery.flot",
|
||||
"jquery.flot.pie": "vendor/flot/jquery.flot.pie",
|
||||
"jquery.flot.selection": "vendor/flot/jquery.flot.selection",
|
||||
"jquery.flot.stack": "vendor/flot/jquery.flot.stack",
|
||||
"jquery.flot.stackpercent": "vendor/flot/jquery.flot.stackpercent",
|
||||
"jquery.flot.time": "vendor/flot/jquery.flot.time",
|
||||
"jquery.flot.crosshair": "vendor/flot/jquery.flot.crosshair",
|
||||
"jquery.flot.fillbelow": "vendor/flot/jquery.flot.fillbelow",
|
||||
"jquery.flot.gauge": "vendor/flot/jquery.flot.gauge",
|
||||
"d3": "vendor/d3/d3.js",
|
||||
"jquery.flot.dashes": "vendor/flot/jquery.flot.dashes",
|
||||
"ace": "vendor/npm/ace-builds/src-noconflict/ace",
|
||||
"clipboard": "vendor/npm/clipboard/dist/clipboard.js"
|
||||
},
|
||||
|
||||
packages: {
|
||||
app: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
vendor: {
|
||||
defaultExtension: 'js',
|
||||
},
|
||||
test: {
|
||||
defaultExtension: 'js',
|
||||
}
|
||||
},
|
||||
|
||||
map: {
|
||||
},
|
||||
|
||||
meta: {
|
||||
'vendor/angular-other/angular-strap.js': {
|
||||
deps: ['angular']
|
||||
},
|
||||
'vendor/npm/angular/angular.js': {
|
||||
format: 'global',
|
||||
deps: ['jquery'],
|
||||
exports: 'angular',
|
||||
},
|
||||
'vendor/npm/angular-mocks/angular-mocks.js': {
|
||||
format: 'global',
|
||||
deps: ['angular'],
|
||||
},
|
||||
'vendor/npm/eventemitter3/index.js': {
|
||||
format: 'cjs',
|
||||
exports: 'EventEmitter'
|
||||
},
|
||||
'vendor/npm/mousetrap/mousetrap.js': {
|
||||
format: 'global',
|
||||
exports: 'Mousetrap'
|
||||
},
|
||||
'vendor/npm/ace-builds/src-noconflict/ace.js': {
|
||||
format: 'global',
|
||||
exports: 'ace'
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
function file2moduleName(filePath) {
|
||||
return filePath.replace(/\\/g, '/')
|
||||
.replace(/^\/base\//, '')
|
||||
.replace(/\.\w*$/, '');
|
||||
}
|
||||
|
||||
function onlySpecFiles(path) {
|
||||
return /specs.*/.test(path);
|
||||
}
|
||||
|
||||
window.grafanaBootData = {settings: {}};
|
||||
|
||||
var modules = ['angular', 'angular-mocks', 'app/app'];
|
||||
var promises = modules.map(function(name) {
|
||||
return System.import(name);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(function(deps) {
|
||||
var angular = deps[0];
|
||||
|
||||
angular.module('grafana', ['ngRoute']);
|
||||
angular.module('grafana.services', ['ngRoute', '$strap.directives']);
|
||||
angular.module('grafana.panels', []);
|
||||
angular.module('grafana.controllers', []);
|
||||
angular.module('grafana.directives', []);
|
||||
angular.module('grafana.filters', []);
|
||||
angular.module('grafana.routes', ['ngRoute']);
|
||||
|
||||
// load specs
|
||||
return Promise.all(
|
||||
Object.keys(window.__karma__.files) // All files served by Karma.
|
||||
.filter(onlySpecFiles)
|
||||
.map(file2moduleName)
|
||||
.map(function(path) {
|
||||
// console.log(path);
|
||||
return System.import(path);
|
||||
}));
|
||||
}).then(function() {
|
||||
window.__karma__.start();
|
||||
}, function(error) {
|
||||
window.__karma__.error(error.stack || error);
|
||||
}).catch(function(error) {
|
||||
window.__karma__.error(error.stack || error);
|
||||
});
|
||||
|
||||
})();
|
10
public/vendor/crypto.min.js
vendored
10
public/vendor/crypto.min.js
vendored
@ -1,10 +0,0 @@
|
||||
/*
|
||||
* Crypto-JS v2.5.3
|
||||
* http://code.google.com/p/crypto-js/
|
||||
* (c) 2009-2012 by Jeff Mott. All rights reserved.
|
||||
* http://code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
(typeof Crypto=="undefined"||!Crypto.util)&&function(){var e=window.Crypto={},g=e.util={rotl:function(a,b){return a<<b|a>>>32-b},rotr:function(a,b){return a<<32-b|a>>>b},endian:function(a){if(a.constructor==Number)return g.rotl(a,8)&16711935|g.rotl(a,24)&4278255360;for(var b=0;b<a.length;b++)a[b]=g.endian(a[b]);return a},randomBytes:function(a){for(var b=[];a>0;a--)b.push(Math.floor(Math.random()*256));return b},bytesToWords:function(a){for(var b=[],c=0,d=0;c<a.length;c++,d+=8)b[d>>>5]|=(a[c]&255)<<
|
||||
24-d%32;return b},wordsToBytes:function(a){for(var b=[],c=0;c<a.length*32;c+=8)b.push(a[c>>>5]>>>24-c%32&255);return b},bytesToHex:function(a){for(var b=[],c=0;c<a.length;c++)b.push((a[c]>>>4).toString(16)),b.push((a[c]&15).toString(16));return b.join("")},hexToBytes:function(a){for(var b=[],c=0;c<a.length;c+=2)b.push(parseInt(a.substr(c,2),16));return b},bytesToBase64:function(a){if(typeof btoa=="function")return btoa(f.bytesToString(a));for(var b=[],c=0;c<a.length;c+=3)for(var d=a[c]<<16|a[c+1]<<
|
||||
8|a[c+2],e=0;e<4;e++)c*8+e*6<=a.length*8?b.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(d>>>6*(3-e)&63)):b.push("=");return b.join("")},base64ToBytes:function(a){if(typeof atob=="function")return f.stringToBytes(atob(a));for(var a=a.replace(/[^A-Z0-9+\/]/ig,""),b=[],c=0,d=0;c<a.length;d=++c%4)d!=0&&b.push(("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(a.charAt(c-1))&Math.pow(2,-2*d+8)-1)<<d*2|"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(a.charAt(c))>>>
|
||||
6-d*2);return b}},e=e.charenc={};e.UTF8={stringToBytes:function(a){return f.stringToBytes(unescape(encodeURIComponent(a)))},bytesToString:function(a){return decodeURIComponent(escape(f.bytesToString(a)))}};var f=e.Binary={stringToBytes:function(a){for(var b=[],c=0;c<a.length;c++)b.push(a.charCodeAt(c)&255);return b},bytesToString:function(a){for(var b=[],c=0;c<a.length;c++)b.push(String.fromCharCode(a[c]));return b.join("")}}}();
|
216
public/vendor/filesaver.js
vendored
216
public/vendor/filesaver.js
vendored
@ -1,216 +0,0 @@
|
||||
/* FileSaver.js
|
||||
* A saveAs() FileSaver implementation.
|
||||
* 2013-01-23
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* License: X11/MIT
|
||||
* See LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self */
|
||||
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||
plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
||||
|
||||
var saveAs = saveAs
|
||||
|| (navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator))
|
||||
|| (function(view) {
|
||||
"use strict";
|
||||
var
|
||||
doc = view.document
|
||||
// only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
|
||||
, get_URL = function() {
|
||||
return view.URL || view.webkitURL || view;
|
||||
}
|
||||
, URL = view.URL || view.webkitURL || view
|
||||
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
||||
, can_use_save_link = !view.externalHost && "download" in save_link
|
||||
, click = function(node) {
|
||||
var event = doc.createEvent("MouseEvents");
|
||||
event.initMouseEvent(
|
||||
"click", true, false, view, 0, 0, 0, 0, 0
|
||||
, false, false, false, false, 0, null
|
||||
);
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
, webkit_req_fs = view.webkitRequestFileSystem
|
||||
, req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem
|
||||
, throw_outside = function (ex) {
|
||||
(view.setImmediate || view.setTimeout)(function() {
|
||||
throw ex;
|
||||
}, 0);
|
||||
}
|
||||
, force_saveable_type = "application/octet-stream"
|
||||
, fs_min_size = 0
|
||||
, deletion_queue = []
|
||||
, process_deletion_queue = function() {
|
||||
var i = deletion_queue.length;
|
||||
while (i--) {
|
||||
var file = deletion_queue[i];
|
||||
if (typeof file === "string") { // file is an object URL
|
||||
URL.revokeObjectURL(file);
|
||||
} else { // file is a File
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
deletion_queue.length = 0; // clear queue
|
||||
}
|
||||
, dispatch = function(filesaver, event_types, event) {
|
||||
event_types = [].concat(event_types);
|
||||
var i = event_types.length;
|
||||
while (i--) {
|
||||
var listener = filesaver["on" + event_types[i]];
|
||||
if (typeof listener === "function") {
|
||||
try {
|
||||
listener.call(filesaver, event || filesaver);
|
||||
} catch (ex) {
|
||||
throw_outside(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
, FileSaver = function(blob, name) {
|
||||
// First try a.download, then web filesystem, then object URLs
|
||||
var
|
||||
filesaver = this
|
||||
, type = blob.type
|
||||
, blob_changed = false
|
||||
, object_url
|
||||
, target_view
|
||||
, get_object_url = function() {
|
||||
var object_url = get_URL().createObjectURL(blob);
|
||||
deletion_queue.push(object_url);
|
||||
return object_url;
|
||||
}
|
||||
, dispatch_all = function() {
|
||||
dispatch(filesaver, "writestart progress write writeend".split(" "));
|
||||
}
|
||||
// on any filesys errors revert to saving with object URLs
|
||||
, fs_error = function() {
|
||||
// don't create more object URLs than needed
|
||||
if (blob_changed || !object_url) {
|
||||
object_url = get_object_url(blob);
|
||||
}
|
||||
if (target_view) {
|
||||
target_view.location.href = object_url;
|
||||
} else {
|
||||
window.open(object_url, "_blank");
|
||||
}
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
}
|
||||
, abortable = function(func) {
|
||||
return function() {
|
||||
if (filesaver.readyState !== filesaver.DONE) {
|
||||
return func.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
, create_if_not_found = {create: true, exclusive: false}
|
||||
, slice
|
||||
;
|
||||
filesaver.readyState = filesaver.INIT;
|
||||
if (!name) {
|
||||
name = "download";
|
||||
}
|
||||
if (can_use_save_link) {
|
||||
object_url = get_object_url(blob);
|
||||
save_link.href = object_url;
|
||||
save_link.download = name;
|
||||
click(save_link);
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch_all();
|
||||
return;
|
||||
}
|
||||
// Object and web filesystem URLs have a problem saving in Google Chrome when
|
||||
// viewed in a tab, so I force save with application/octet-stream
|
||||
// http://code.google.com/p/chromium/issues/detail?id=91158
|
||||
if (view.chrome && type && type !== force_saveable_type) {
|
||||
slice = blob.slice || blob.webkitSlice;
|
||||
blob = slice.call(blob, 0, blob.size, force_saveable_type);
|
||||
blob_changed = true;
|
||||
}
|
||||
// Since I can't be sure that the guessed media type will trigger a download
|
||||
// in WebKit, I append .download to the filename.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=65440
|
||||
if (webkit_req_fs && name !== "download") {
|
||||
name += ".download";
|
||||
}
|
||||
if (type === force_saveable_type || webkit_req_fs) {
|
||||
target_view = view;
|
||||
}
|
||||
if (!req_fs) {
|
||||
fs_error();
|
||||
return;
|
||||
}
|
||||
fs_min_size += blob.size;
|
||||
req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) {
|
||||
fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) {
|
||||
var save = function() {
|
||||
dir.getFile(name, create_if_not_found, abortable(function(file) {
|
||||
file.createWriter(abortable(function(writer) {
|
||||
writer.onwriteend = function(event) {
|
||||
target_view.location.href = file.toURL();
|
||||
deletion_queue.push(file);
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "writeend", event);
|
||||
};
|
||||
writer.onerror = function() {
|
||||
var error = writer.error;
|
||||
if (error.code !== error.ABORT_ERR) {
|
||||
fs_error();
|
||||
}
|
||||
};
|
||||
"writestart progress write abort".split(" ").forEach(function(event) {
|
||||
writer["on" + event] = filesaver["on" + event];
|
||||
});
|
||||
writer.write(blob);
|
||||
filesaver.abort = function() {
|
||||
writer.abort();
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
};
|
||||
filesaver.readyState = filesaver.WRITING;
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
};
|
||||
dir.getFile(name, {create: false}, abortable(function(file) {
|
||||
// delete file if it already exists
|
||||
file.remove();
|
||||
save();
|
||||
}), abortable(function(ex) {
|
||||
if (ex.code === ex.NOT_FOUND_ERR) {
|
||||
save();
|
||||
} else {
|
||||
fs_error();
|
||||
}
|
||||
}));
|
||||
}), fs_error);
|
||||
}), fs_error);
|
||||
}
|
||||
, FS_proto = FileSaver.prototype
|
||||
, saveAs = function(blob, name) {
|
||||
return new FileSaver(blob, name);
|
||||
}
|
||||
;
|
||||
FS_proto.abort = function() {
|
||||
var filesaver = this;
|
||||
filesaver.readyState = filesaver.DONE;
|
||||
dispatch(filesaver, "abort");
|
||||
};
|
||||
FS_proto.readyState = FS_proto.INIT = 0;
|
||||
FS_proto.WRITING = 1;
|
||||
FS_proto.DONE = 2;
|
||||
|
||||
FS_proto.error =
|
||||
FS_proto.onwritestart =
|
||||
FS_proto.onprogress =
|
||||
FS_proto.onwrite =
|
||||
FS_proto.onabort =
|
||||
FS_proto.onerror =
|
||||
FS_proto.onwriteend =
|
||||
null;
|
||||
|
||||
view.addEventListener("unload", process_deletion_queue, false);
|
||||
return saveAs;
|
||||
}(self));
|
70
public/vendor/license.json
vendored
70
public/vendor/license.json
vendored
@ -1,70 +0,0 @@
|
||||
{
|
||||
"angular": {
|
||||
"version":"1.1.5",
|
||||
"license":"MIT"
|
||||
},
|
||||
"angular-dragdrop": {
|
||||
"version":"1.0.4",
|
||||
"license":"MIT"
|
||||
},
|
||||
"angular-strap": {
|
||||
"version":"0.7.5",
|
||||
"license":"MIT"
|
||||
},
|
||||
"bindonce": {
|
||||
"version":"0.2.1",
|
||||
"license":"MIT"
|
||||
},
|
||||
"datepicker": {
|
||||
"version":"12/3/2013",
|
||||
"license":"Apache 2.0"
|
||||
},
|
||||
"timepicker": {
|
||||
"version":"0.2.6",
|
||||
"license":"MIT"
|
||||
},
|
||||
"bootstrap": {
|
||||
"version":"2.3.2",
|
||||
"license":"Apache 2.0"
|
||||
},
|
||||
"elasticjs": {
|
||||
"version":"1.1.1",
|
||||
"license":"MIT"
|
||||
},
|
||||
"jquery": {
|
||||
"version":"1.8.0",
|
||||
"license":"MIT"
|
||||
},
|
||||
"jquery-ui": {
|
||||
"version":"1.10.3",
|
||||
"license":"MIT"
|
||||
},
|
||||
"flot": {
|
||||
"version":"0.8.1",
|
||||
"license":"MIT"
|
||||
},
|
||||
"require": {
|
||||
"version":"2.1.8",
|
||||
"license":"MIT"
|
||||
},
|
||||
"filesaver": {
|
||||
"version":"2013-01-23",
|
||||
"license":"MIT"
|
||||
},
|
||||
"modernizr": {
|
||||
"version":"2.6.1",
|
||||
"license":"MIT"
|
||||
},
|
||||
"moment": {
|
||||
"version":"2.1.0",
|
||||
"license":"MIT"
|
||||
},
|
||||
"timezone": {
|
||||
"version":"2010",
|
||||
"license":"Apache 2"
|
||||
},
|
||||
"underscore": {
|
||||
"version":"1.5.1",
|
||||
"license":"MIT"
|
||||
}
|
||||
}
|
11954
public/vendor/moment.js
vendored
11954
public/vendor/moment.js
vendored
File diff suppressed because it is too large
Load Diff
@ -6,9 +6,10 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<title>Grafana</title>
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
|
||||
<link rel="stylesheet" href="[[.AppSubUrl]]/css/grafana.dark.min.css" title="Dark">
|
||||
<link rel="icon" type="image/png" href="[[.AppSubUrl]]/img/fav32.png">
|
||||
<link rel="stylesheet" href="public/build/grafana.dark.min.css" title="Dark">
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -5,11 +5,14 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Grafana - Error</title>
|
||||
<link href='[[.AppSubUrl]]/public/css/fonts.min.css' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="[[.AppSubUrl]]/public/css/grafana.dark.min.css">
|
||||
<link rel="icon" type="image/png" href="[[.AppSubUrl]]/public/img/fav32.png">
|
||||
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
|
||||
<link href='public/css/fonts.min.css' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="public/build/grafana.dark.min.css">
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
|
@ -8,18 +8,16 @@
|
||||
|
||||
<title>Grafana</title>
|
||||
|
||||
<link href='[[.AppSubUrl]]/public/css/fonts.min.css' rel='stylesheet' type='text/css'>
|
||||
|
||||
[[if .User.LightTheme]]
|
||||
<link rel="stylesheet" href="[[.AppSubUrl]]/public/css/grafana.light.min.css">
|
||||
[[else]]
|
||||
<link rel="stylesheet" href="[[.AppSubUrl]]/public/css/grafana.dark.min.css">
|
||||
[[end]]
|
||||
|
||||
<link rel="icon" type="image/png" href="[[.AppSubUrl]]/public/img/fav32.png">
|
||||
<link rel="mask-icon" href="[[.AppSubUrl]]/public/img/grafana_mask_icon.svg" color="#F05A28">
|
||||
<base href="[[.AppSubUrl]]/" />
|
||||
|
||||
[[if .User.LightTheme]]
|
||||
<link rel="stylesheet" href="public/build/grafana.light.css?v[[ .BuildVersion ]]">
|
||||
[[else]]
|
||||
<link rel="stylesheet" href="public/build/grafana.dark.css?v[[ .BuildVersion ]]">
|
||||
[[end]]
|
||||
|
||||
<link rel="icon" type="image/png" href="public/img/fav32.png">
|
||||
<link rel="mask-icon" href="public/img/grafana_mask_icon.svg" color="#F05A28">
|
||||
</head>
|
||||
|
||||
<body ng-cloak>
|
||||
@ -88,13 +86,6 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- build:js [[.AppSubUrl]]/public/app/boot.js -->
|
||||
<script src="[[.AppSubUrl]]/public/vendor/npm/es6-shim/es6-shim.js"></script>
|
||||
<script src="[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system.src.js"></script>
|
||||
<script src="[[.AppSubUrl]]/public/app/system.conf.js"></script>
|
||||
<script src="[[.AppSubUrl]]/public/app/boot.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
[[if .GoogleTagManagerId]]
|
||||
<script>
|
||||
dataLayer = [{
|
13
scripts/grunt/build_task.js
Normal file
13
scripts/grunt/build_task.js
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
module.exports = function(grunt) {
|
||||
"use strict";
|
||||
|
||||
// Concat and Minify the src directory into dist
|
||||
grunt.registerTask('build', [
|
||||
'clean:release',
|
||||
'clean:build',
|
||||
'phantomjs',
|
||||
'webpack:prod',
|
||||
]);
|
||||
|
||||
};
|
@ -2,30 +2,21 @@
|
||||
module.exports = function(grunt) {
|
||||
'use strict';
|
||||
|
||||
grunt.registerTask('css', [
|
||||
'sass',
|
||||
'concat:cssDark',
|
||||
'concat:cssLight',
|
||||
'concat:cssFonts',
|
||||
'styleguide',
|
||||
'sasslint',
|
||||
'postcss',
|
||||
]
|
||||
);
|
||||
|
||||
grunt.registerTask('default', [
|
||||
'clean:gen',
|
||||
'jscs',
|
||||
'jshint',
|
||||
'copy:node_modules',
|
||||
'copy:public_to_gen',
|
||||
'exec:tslint',
|
||||
'clean:build',
|
||||
'phantomjs',
|
||||
'css',
|
||||
'exec:tscompile'
|
||||
'webpack:dev',
|
||||
]);
|
||||
|
||||
grunt.registerTask('test', ['default', 'karma:test', 'no-only-tests']);
|
||||
grunt.registerTask('test', [
|
||||
'clean:build',
|
||||
'jscs',
|
||||
'jshint',
|
||||
'sasslint',
|
||||
'exec:tslint',
|
||||
'karma:test',
|
||||
'no-only-tests'
|
||||
]);
|
||||
|
||||
grunt.registerTask('no-only-tests', function() {
|
||||
var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');
|
11
scripts/grunt/options/clean.js
Normal file
11
scripts/grunt/options/clean.js
Normal file
@ -0,0 +1,11 @@
|
||||
module.exports = function(config) {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
release: ['<%= destDir %>', '<%= tempDir %>', '<%= genDir %>'],
|
||||
build: ['<%= srcDir %>/build'],
|
||||
temp: ['<%= tempDir %>'],
|
||||
packaging: [
|
||||
],
|
||||
};
|
||||
};
|
6
scripts/grunt/options/exec.js
Normal file
6
scripts/grunt/options/exec.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = function(config, grunt) {
|
||||
'use strict'
|
||||
return {
|
||||
tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json --type-check",
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user