Merge branch 'master' of github.com:elasticsearch/kibana into fixitfriday/auto-select-first
This commit is contained in:
commit
706aa3c3f1
4
FAQ.md
4
FAQ.md
|
@ -11,3 +11,7 @@
|
|||
|
||||
**Q:** What happened to templated/scripted dashboards?
|
||||
**A:** Check out the URL. The state of each app is stored there, including any filters, queries or columns. This should be a lot easier than constructing scripted dashboards. The encoding of the URL is RISON.
|
||||
|
||||
**Q:** I'm getting `bin/node/bin/node: not found` but I can see the node binary in the package?
|
||||
**A:** Kibana 4 packages are architecture specific. Ensure you are using the correct package for your architecture.
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
[[production]]
|
||||
== Using Kibana in a Production Environment
|
||||
When you set up Kibana in a production environment, rather than on your local
|
||||
machine, you need to consider:
|
||||
* <<configuring-kibana-shield, Configuring Kibana to Work with Shield>>
|
||||
* <<enabling-ssl, Enabling SSL>>
|
||||
* <<controlling-access, Controlling Access>>
|
||||
* <<load-balancing, Load Balancing Across Multiple Elasticsearch Nodes>>
|
||||
|
||||
* Where you are going to run Kibana.
|
||||
* Whether you need to encrypt communications to and from Kibana.
|
||||
* If you need to control access to your data.
|
||||
|
||||
=== Deployment Considerations
|
||||
How you deploy Kibana largely depends on your use case. If you are the only user,
|
||||
you can run Kibana on your local machine and configure it to point to whatever
|
||||
Elasticsearch instance you want to interact with. Conversely, if you have a large
|
||||
|
@ -15,35 +12,19 @@ number of heavy Kibana users, you might need to load balance across multiple
|
|||
Kibana instances that are all connected to the same Elasticsearch instance.
|
||||
|
||||
While Kibana isn't terribly resource intensive, we still recommend running Kibana
|
||||
on its own node, rather than on one of your Elasticsearch nodes.
|
||||
separate from your Elasticsearch data or master nodes. To distribute Kibana
|
||||
traffic across the nodes in your Elasticsearch cluster, you can run Kibana
|
||||
and an Elasticsearch client node on the same machine. For more information, see
|
||||
<<load-balancing, Load Balancing Across Multiple Elasticsearch Nodes>>.
|
||||
|
||||
[float]
|
||||
[[configuring-kibana-shield]]
|
||||
=== Configuring Kibana to Work with Shield
|
||||
If you are using Shield to authenticate Elasticsearch users, you need to provide
|
||||
Kibana with user credentials so it can access the `.kibana` index. The Kibana user
|
||||
needs permission to perform the following actions on the `.kibana` index:
|
||||
the Kibana server with credentials so it can access the `.kibana` index and monitor
|
||||
the cluster.
|
||||
|
||||
----
|
||||
'.kibana':
|
||||
- indices:admin/create
|
||||
- indices:admin/exists
|
||||
- indices:admin/mapping/put
|
||||
- indices:admin/mappings/fields/get
|
||||
- indices:admin/refresh
|
||||
- indices:admin/validate/query
|
||||
- indices:data/read/get
|
||||
- indices:data/read/mget
|
||||
- indices:data/read/search
|
||||
- indices:data/write/delete
|
||||
- indices:data/write/index
|
||||
- indices:data/write/update
|
||||
- indices:admin/create
|
||||
----
|
||||
|
||||
For more information about configuring access in Shield,
|
||||
see https://www.elasticsearch.org/guide/en/shield/current/_shield_with_kibana_4.html[Shield with Kibana 4]
|
||||
in the Shield documentation.
|
||||
|
||||
To configure credentials for Kibana, set the `kibana_elasticsearch_username` and
|
||||
To configure credentials the Kibana server, set the `kibana_elasticsearch_username` and
|
||||
`kibana_elasticsearch_password` properties in `kibana.yml`:
|
||||
|
||||
----
|
||||
|
@ -51,6 +32,13 @@ To configure credentials for Kibana, set the `kibana_elasticsearch_username` and
|
|||
kibana_elasticsearch_username: kibana4
|
||||
kibana_elasticsearch_password: kibana4
|
||||
----
|
||||
|
||||
For information about assigning the Kibana server the necessary permissions in Shield,
|
||||
see https://www.elasticsearch.org/guide/en/shield/current/_shield_with_kibana_4.html[Shield with Kibana 4]
|
||||
in the Shield documentation.
|
||||
|
||||
[float]
|
||||
[[enabling-ssl]]
|
||||
=== Enabling SSL
|
||||
Kibana supports SSL encryption for both client requests and the requests the Kibana server
|
||||
sends to Elasticsearch.
|
||||
|
@ -82,6 +70,8 @@ If you are using a self-signed certificate for Elasticsearch, set the `ca` prope
|
|||
ca: /path/to/your/ca/cacert.pem
|
||||
----
|
||||
|
||||
[float]
|
||||
[[controlling-access]]
|
||||
=== Controlling access
|
||||
You can use http://www.elasticsearch.org/overview/shield/[Elasticsearch Shield]
|
||||
(Shield) to control what Elasticsearch data users can access through Kibana.
|
||||
|
@ -89,6 +79,47 @@ Shield provides index-level access control. If a user isn't authorized to run
|
|||
the query that populates a Kibana visualization, the user just sees an empty
|
||||
visualization.
|
||||
|
||||
To configure access to Kibana using Shield, you create one or more Shield roles
|
||||
To configure access to Kibana using Shield, you create Shield roles
|
||||
for Kibana using the `kibana4` default role as a starting point. For more
|
||||
information, see http://www.elasticsearch.org/guide/en/shield/current/_shield_with_kibana_4.html[Using Shield with Kibana 4].
|
||||
information, see http://www.elasticsearch.org/guide/en/shield/current/_shield_with_kibana_4.html[Using Shield with Kibana 4].
|
||||
|
||||
[float]
|
||||
[[load-balancing]]
|
||||
=== Load Balancing Across Multiple Elasticsearch Nodes
|
||||
If you have multiple nodes in your Elasticsearch cluster, the easiest way to distribute Kibana requests
|
||||
across the nodes is to run an Elasticsearch _client_ node on the same machine as Kibana.
|
||||
Elasticsearch client nodes are essentially smart load balancers that are part of the cluster. They
|
||||
process incoming HTTP requests, redirect operations to the other nodes in the cluster as needed, and
|
||||
gather and return the results. For more information, see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-node.html[Node] in the Elasticsearch reference.
|
||||
|
||||
To use a local client node to load balance Kibana requests:
|
||||
|
||||
. Install Elasticsearch on the same machine as Kibana.
|
||||
. Configure the node as a client node. In `elasticsearch.yml`, set both `node.data` and `node.master` to `false`:
|
||||
+
|
||||
--------
|
||||
# 3. You want this node to be neither master nor data node, but
|
||||
# to act as a "search load balancer" (fetching data from nodes,
|
||||
# aggregating results, etc.)
|
||||
#
|
||||
node.master: false
|
||||
node.data: false
|
||||
--------
|
||||
. Configure the client node to join your Elasticsearch cluster. In `elasticsearch.yml`, set the `cluster.name` to the
|
||||
name of your cluster.
|
||||
+
|
||||
--------
|
||||
cluster.name: "my_cluster"
|
||||
--------
|
||||
. Make sure Kibana is configured to point to your local client node. In `kibana.yml`, the `elasticsearch_url` should be set to
|
||||
`localhost:9200`.
|
||||
+
|
||||
--------
|
||||
# The Elasticsearch instance to use for all your queries.
|
||||
elasticsearch_url: "http://localhost:9200"
|
||||
--------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@
|
|||
"request": "^2.40.0",
|
||||
"requirefrom": "^0.2.0",
|
||||
"semver": "^4.2.0",
|
||||
"serve-favicon": "~2.2.0"
|
||||
"serve-favicon": "~2.2.0",
|
||||
"through": "^2.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"connect": "~2.19.5",
|
||||
|
@ -96,6 +97,8 @@
|
|||
"requirejs": "~2.1.14",
|
||||
"rjs-build-analysis": "0.0.3",
|
||||
"simple-git": "^0.11.0",
|
||||
"sinon": "^1.12.2",
|
||||
"sinon-as-promised": "^2.0.3",
|
||||
"tar": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,11 @@
|
|||
</td>
|
||||
|
||||
<td>
|
||||
<i bo-if="!mapping[field] && !showArrayInObjectsWarning(doc, field)"
|
||||
<i bo-if="!mapping[field] && field[0] === '_'"
|
||||
tooltip-placement="top"
|
||||
tooltip="Field names beginning with _ are not supported"
|
||||
class="fa fa-warning text-color-warning ng-scope doc-viewer-underscore"></i>
|
||||
<i bo-if="!mapping[field] && field[0] !== '_' && !showArrayInObjectsWarning(doc, field)"
|
||||
tooltip-placement="top"
|
||||
tooltip="No cached mapping for this field. Refresh your mapping from the Settings > Indices page"
|
||||
class="fa fa-warning text-color-warning ng-scope doc-viewer-no-mapping"></i>
|
||||
|
@ -43,4 +47,4 @@
|
|||
|
||||
<pre ng-show="mode == 'json'">{{hit | json}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -27,6 +27,9 @@ define(function (require) {
|
|||
$scope.formatted = _.mapValues($scope.flattened, function (value, name) {
|
||||
var mapping = $scope.mapping[name];
|
||||
var formatter = (mapping && mapping.format) ? mapping.format : defaultFormat;
|
||||
if (_.isArray(value) && typeof value[0] === 'object') {
|
||||
value = JSON.stringify(value, null, ' ');
|
||||
}
|
||||
return formatter.convert(value);
|
||||
});
|
||||
$scope.fields = _.keys($scope.flattened).sort();
|
||||
|
@ -39,4 +42,4 @@ define(function (require) {
|
|||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -91,12 +91,14 @@
|
|||
</div>
|
||||
<br>
|
||||
<div class="small">
|
||||
<input
|
||||
ng-model="relative.round"
|
||||
ng-checked="relative.round"
|
||||
ng-change="formatRelative()"
|
||||
type="checkbox">
|
||||
round to the {{units[relative.unit]}}
|
||||
<label>
|
||||
<input
|
||||
ng-model="relative.round"
|
||||
ng-checked="relative.round"
|
||||
ng-change="formatRelative()"
|
||||
type="checkbox">
|
||||
round to the {{units[relative.unit]}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -169,7 +171,11 @@
|
|||
<br>
|
||||
<div ng-repeat="list in refreshLists" class="kbn-refresh-section">
|
||||
<ul class="list-unstyled">
|
||||
<li ng-repeat="interval in list"><a ng-click="setRefreshInterval(interval)">{{interval.display}}</a></li>
|
||||
<li ng-repeat="inter in list">
|
||||
<a class="refresh-interval" ng-class="{ 'refresh-interval-active': interval.value == inter.value }" ng-click="setRefreshInterval(inter)">
|
||||
{{inter.display}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,7 @@ define(function (require) {
|
|||
|
||||
this.handler = handler;
|
||||
this.dispatch = d3.dispatch('brush', 'click', 'hover', 'mouseup',
|
||||
'mousedown', 'mouseover');
|
||||
'mousedown', 'mouseover', 'mouseout');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,6 +92,7 @@ define(function (require) {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @method addHoverEvent
|
||||
|
@ -101,6 +102,7 @@ define(function (require) {
|
|||
var self = this;
|
||||
var isClickable = (this.dispatch.on('click'));
|
||||
var addEvent = this.addEvent;
|
||||
var $el = this.handler.el;
|
||||
|
||||
function hover(d, i) {
|
||||
d3.event.stopPropagation();
|
||||
|
@ -110,12 +112,32 @@ define(function (require) {
|
|||
self.addMousePointer.call(this, arguments);
|
||||
}
|
||||
|
||||
self.highlightLegend.call(this, $el);
|
||||
self.dispatch.hover.call(this, self.eventResponse(d, i));
|
||||
}
|
||||
|
||||
return addEvent('mouseover', hover);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @method addMouseoutEvent
|
||||
* @returns {Function}
|
||||
*/
|
||||
Dispatch.prototype.addMouseoutEvent = function () {
|
||||
var self = this;
|
||||
var addEvent = this.addEvent;
|
||||
var $el = this.handler.el;
|
||||
|
||||
function mouseout() {
|
||||
d3.event.stopPropagation();
|
||||
|
||||
self.unHighlightLegend.call(this, $el);
|
||||
}
|
||||
|
||||
return addEvent('mouseout', mouseout);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @method addClickEvent
|
||||
|
@ -189,7 +211,7 @@ define(function (require) {
|
|||
|
||||
|
||||
/**
|
||||
* Mouse over Behavior
|
||||
* Mouseover Behavior
|
||||
*
|
||||
* @method addMousePointer
|
||||
* @returns {D3.Selection}
|
||||
|
@ -198,6 +220,38 @@ define(function (require) {
|
|||
return d3.select(this).style('cursor', 'pointer');
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouseover Behavior
|
||||
*
|
||||
* @param element {D3.Selection}
|
||||
* @method highlightLegend
|
||||
*/
|
||||
Dispatch.prototype.highlightLegend = function (element) {
|
||||
var classList = d3.select(this).node().classList;
|
||||
var liClass = d3.select(this).node().classList[1];
|
||||
|
||||
d3.select(element)
|
||||
.select('.legend-ul')
|
||||
.selectAll('li.color')
|
||||
.filter(function (d, i) {
|
||||
return d3.select(this).node().classList[1] !== liClass;
|
||||
})
|
||||
.classed('blur_shape', true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouseout Behavior
|
||||
*
|
||||
* @param element {D3.Selection}
|
||||
* @method unHighlightLegend
|
||||
*/
|
||||
Dispatch.prototype.unHighlightLegend = function (element) {
|
||||
d3.select(element)
|
||||
.select('.legend-ul')
|
||||
.selectAll('li.color')
|
||||
.classed('blur_shape', false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds D3 brush to SVG and returns the brush function
|
||||
*
|
||||
|
|
|
@ -85,14 +85,16 @@ define(function (require) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Creates a class name based on the colors assigned to each label
|
||||
* Creates a class name based on the hexColor assigned to each label
|
||||
*
|
||||
* @method colorToClass
|
||||
* @param name {String} Label
|
||||
* @param hexColor {String} Label
|
||||
* @returns {string} CSS class name
|
||||
*/
|
||||
Legend.prototype.colorToClass = function (name) {
|
||||
return 'c' + name.replace(/[#]/g, '');
|
||||
Legend.prototype.colorToClass = function (hexColor) {
|
||||
if (hexColor) {
|
||||
return 'c' + hexColor.replace(/[#]/g, '');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -131,35 +133,70 @@ define(function (require) {
|
|||
}
|
||||
});
|
||||
|
||||
visEl.selectAll('.color')
|
||||
legendDiv.select('.legend-ul').selectAll('li')
|
||||
.on('mouseover', function (d) {
|
||||
var liClass = '.' + self.colorToClass(self.color(d));
|
||||
visEl.selectAll('.color').classed('blur_shape', true);
|
||||
var liClass = self.colorToClass(self.color(d));
|
||||
var charts = visEl.selectAll('.chart');
|
||||
|
||||
// legend
|
||||
legendDiv.selectAll('li')
|
||||
.filter(function (d) {
|
||||
return d3.select(this).node().classList[1] !== liClass;
|
||||
})
|
||||
.classed('blur_shape', true);
|
||||
|
||||
// lines/area
|
||||
charts.selectAll('.color')
|
||||
.filter(function (d) {
|
||||
return d3.select(this).node().classList[1] !== liClass;
|
||||
})
|
||||
.classed('blur_shape', true);
|
||||
|
||||
// circles
|
||||
charts.selectAll('.line circle')
|
||||
.filter(function (d) {
|
||||
return d3.select(this).node().classList[1] !== liClass;
|
||||
})
|
||||
.classed('blur_shape', true);
|
||||
|
||||
// pie slices
|
||||
charts.selectAll('.slice')
|
||||
.filter(function (d) {
|
||||
return d3.select(this).node().classList[1] !== liClass;
|
||||
})
|
||||
.classed('blur_shape', true);
|
||||
|
||||
var eventEl = d3.select(this);
|
||||
eventEl.style('white-space', 'inherit');
|
||||
eventEl.style('word-break', 'break-all');
|
||||
|
||||
// select series on chart
|
||||
visEl.selectAll(liClass).classed('blur_shape', false);
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
/*
|
||||
* The default opacity of elements in charts may be modified by the
|
||||
* chart constructor, and so may differ from that of the legend
|
||||
*/
|
||||
visEl.selectAll('.chart')
|
||||
.selectAll('.color')
|
||||
|
||||
var charts = visEl.selectAll('.chart');
|
||||
|
||||
// legend
|
||||
legendDiv.selectAll('li')
|
||||
.classed('blur_shape', false);
|
||||
|
||||
// lines/areas
|
||||
charts.selectAll('.color')
|
||||
.classed('blur_shape', false);
|
||||
|
||||
// circles
|
||||
charts.selectAll('.line circle')
|
||||
.classed('blur_shape', false);
|
||||
|
||||
// pie slices
|
||||
charts.selectAll('.slice')
|
||||
.classed('blur_shape', false);
|
||||
|
||||
var eventEl = d3.select(this);
|
||||
eventEl.style('white-space', 'nowrap');
|
||||
eventEl.style('word-break', 'inherit');
|
||||
|
||||
// Legend values should always return to their default opacity of 1
|
||||
visEl.select('.legend-ul')
|
||||
.selectAll('.color')
|
||||
.classed('blur_shape', false);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ define(function (require) {
|
|||
* @returns {String} CSS class name
|
||||
*/
|
||||
Chart.prototype.colorToClass = function (label) {
|
||||
return 'color ' + Legend.prototype.colorToClass.call(null, label);
|
||||
return Legend.prototype.colorToClass.call(null, label);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -96,7 +96,7 @@ define(function (require) {
|
|||
// Append path
|
||||
path = layer.append('path')
|
||||
.attr('class', function (d) {
|
||||
return self.colorToClass(color(d[0].label));
|
||||
return 'color ' + self.colorToClass(color(d[0].label));
|
||||
})
|
||||
.style('fill', function (d) {
|
||||
return color(d[0].label);
|
||||
|
@ -125,8 +125,9 @@ define(function (require) {
|
|||
var isBrushable = events.isBrushable();
|
||||
var brush = isBrushable ? events.addBrushEvent(svg) : undefined;
|
||||
var hover = events.addHoverEvent();
|
||||
var mouseout = events.addMouseoutEvent();
|
||||
var click = events.addClickEvent();
|
||||
var attachedEvents = element.call(hover).call(click);
|
||||
var attachedEvents = element.call(hover).call(mouseout).call(click);
|
||||
|
||||
if (isBrushable) {
|
||||
attachedEvents.call(brush);
|
||||
|
@ -144,6 +145,7 @@ define(function (require) {
|
|||
* @returns {D3.UpdateSelection} SVG with circles added
|
||||
*/
|
||||
AreaChart.prototype.addCircles = function (svg, data) {
|
||||
var self = this;
|
||||
var color = this.handler.data.getColorFunc();
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
|
@ -162,7 +164,7 @@ define(function (require) {
|
|||
.append('g')
|
||||
.attr('class', 'points area');
|
||||
|
||||
// Append the bars
|
||||
// append the bars
|
||||
circles = layer
|
||||
.selectAll('rect')
|
||||
.data(function appendData(data) {
|
||||
|
@ -179,7 +181,7 @@ define(function (require) {
|
|||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', function circleClass(d) {
|
||||
return d.label;
|
||||
return d.label + ' ' + self.colorToClass(color(d.label));
|
||||
})
|
||||
.attr('stroke', function strokeColor(d) {
|
||||
return color(d.label);
|
||||
|
|
|
@ -69,7 +69,7 @@ define(function (require) {
|
|||
.enter()
|
||||
.append('rect')
|
||||
.attr('class', function (d) {
|
||||
return self.colorToClass(color(d.label));
|
||||
return 'color ' + self.colorToClass(color(d.label));
|
||||
})
|
||||
.attr('fill', function (d) {
|
||||
return color(d.label);
|
||||
|
@ -246,8 +246,9 @@ define(function (require) {
|
|||
var isBrushable = events.isBrushable();
|
||||
var brush = isBrushable ? events.addBrushEvent(svg) : undefined;
|
||||
var hover = events.addHoverEvent();
|
||||
var mouseout = events.addMouseoutEvent();
|
||||
var click = events.addClickEvent();
|
||||
var attachedEvents = element.call(hover).call(click);
|
||||
var attachedEvents = element.call(hover).call(mouseout).call(click);
|
||||
|
||||
if (isBrushable) {
|
||||
attachedEvents.call(brush);
|
||||
|
|
|
@ -45,8 +45,9 @@ define(function (require) {
|
|||
var isBrushable = events.isBrushable();
|
||||
var brush = isBrushable ? events.addBrushEvent(svg) : undefined;
|
||||
var hover = events.addHoverEvent();
|
||||
var mouseout = events.addMouseoutEvent();
|
||||
var click = events.addClickEvent();
|
||||
var attachedEvents = element.call(hover).call(click);
|
||||
var attachedEvents = element.call(hover).call(mouseout).call(click);
|
||||
|
||||
if (isBrushable) {
|
||||
attachedEvents.call(brush);
|
||||
|
@ -65,6 +66,7 @@ define(function (require) {
|
|||
*/
|
||||
LineChart.prototype.addCircles = function (svg, data) {
|
||||
var self = this;
|
||||
var showCircles = this._attr.showCircles;
|
||||
var color = this.handler.data.getColorFunc();
|
||||
var xScale = this.handler.xAxis.xScale;
|
||||
var yScale = this.handler.yAxis.yScale;
|
||||
|
@ -81,7 +83,7 @@ define(function (require) {
|
|||
.attr('class', 'points line');
|
||||
|
||||
var circles = layer
|
||||
.selectAll('rect')
|
||||
.selectAll('circle')
|
||||
.data(function appendData(d) {
|
||||
return d;
|
||||
});
|
||||
|
@ -105,14 +107,26 @@ define(function (require) {
|
|||
return color(d.label);
|
||||
}
|
||||
|
||||
function colorCircle(d) {
|
||||
var parent = d3.select(this).node().parentNode;
|
||||
var lengthOfParent = d3.select(parent).data()[0].length;
|
||||
var isVisible = (lengthOfParent === 1);
|
||||
|
||||
// If only 1 point exists, show circle
|
||||
if (!showCircles && !isVisible) return 'none';
|
||||
return cColor(d);
|
||||
}
|
||||
|
||||
circles
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('r', visibleRadius)
|
||||
.attr('cx', cx)
|
||||
.attr('cy', cy)
|
||||
.attr('fill', cColor)
|
||||
.attr('class', 'circle-decoration');
|
||||
.attr('fill', colorCircle)
|
||||
.attr('class', function circleClass(d) {
|
||||
return 'circle-decoration ' + self.colorToClass(color(d.label));
|
||||
});
|
||||
|
||||
circles
|
||||
.enter()
|
||||
|
@ -172,7 +186,7 @@ define(function (require) {
|
|||
|
||||
lines.append('path')
|
||||
.attr('class', function lineClass(d) {
|
||||
return self.colorToClass(color(d.label));
|
||||
return 'color ' + self.colorToClass(color(d.label));
|
||||
})
|
||||
.attr('d', function lineD(d) {
|
||||
return line(d.values);
|
||||
|
@ -299,7 +313,6 @@ define(function (require) {
|
|||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', lineStrokeWidth);
|
||||
|
||||
|
||||
return svg;
|
||||
});
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ define(function (require) {
|
|||
|
||||
return element
|
||||
.call(events.addHoverEvent())
|
||||
.call(events.addMouseoutEvent())
|
||||
.call(events.addClickEvent());
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ define(function (require) {
|
|||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
module.directive('cssTruncate', function ($compile) {
|
||||
module.directive('cssTruncate', function ($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
|
@ -16,11 +16,18 @@ define(function (require) {
|
|||
'word-break': 'break-all',
|
||||
});
|
||||
|
||||
if (!_.isUndefined(attrs.cssTruncateExpandable)) {
|
||||
$elem.css({'cursor': 'pointer'});
|
||||
$elem.bind('click', function () {
|
||||
$scope.toggle();
|
||||
});
|
||||
if (attrs.cssTruncateExpandable != null) {
|
||||
$scope.$watch(
|
||||
function () { return $elem.html(); },
|
||||
function () {
|
||||
if ($elem[0].offsetWidth < $elem[0].scrollWidth) {
|
||||
$elem.css({'cursor': 'pointer'});
|
||||
$elem.bind('click', function () {
|
||||
$scope.toggle();
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$scope.toggle = function () {
|
||||
|
|
|
@ -6,7 +6,8 @@ define(function (require) {
|
|||
restrict: 'A',
|
||||
link: function ($scope, $elem, attrs) {
|
||||
$timeout(function () {
|
||||
$elem[0].focus();
|
||||
$elem.focus();
|
||||
if (attrs.inputFocus === 'select') $elem.select();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
<div class="input-group"
|
||||
ng-class="queryInput.$invalid ? 'has-error' : ''">
|
||||
|
||||
<input type="text" input-focus
|
||||
<input type="text"
|
||||
placeholder="Filter..."
|
||||
class="form-control"
|
||||
ng-model="state.query"
|
||||
input-focus
|
||||
kbn-typeahead-input
|
||||
validate-query>
|
||||
<button type="submit" class="btn btn-default" ng-disabled="queryInput.$invalid">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<form ng-submit="opts.save()">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1">Save As</label>
|
||||
<input type="text" ng-model="opts.dashboard.title" class="form-control" placeholder="Dashboard title" input-focus>
|
||||
<input type="text" ng-model="opts.dashboard.title" class="form-control" placeholder="Dashboard title" input-focus="select">
|
||||
</div>
|
||||
<button type="submit" ng-disabled="!opts.dashboard.title" class="btn btn-primary">Save</button>
|
||||
</form>
|
|
@ -38,11 +38,11 @@
|
|||
class="sidebar-item-button primary">
|
||||
Visualize
|
||||
<span class="discover-field-vis-warning" ng-show="warnings.length" tooltip="{{warnings.join(' ')}}">
|
||||
( {{warnings.length}} warnings <i class="fa fa-warning"></i> )
|
||||
( {{warnings.length}} <ng-pluralize count="warnings.length" when="{'1':'warning', 'other':'warnings'}"></ng-pluralize> <i class="fa fa-warning"></i> )
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div ng-show="!field.indexed && !field.scripted"
|
||||
disabled="disabled"
|
||||
tooltip="This field is not indexed thus unavailable for visualization and search"
|
||||
class="sidebar-item-button primary">Not Indexed</div>
|
||||
class="sidebar-item-button primary">Not Indexed</div>
|
||||
|
|
|
@ -37,7 +37,8 @@ define(function (require) {
|
|||
'type',
|
||||
'indexed',
|
||||
'analyzed',
|
||||
'missing'
|
||||
'missing',
|
||||
'name'
|
||||
],
|
||||
defaults: {
|
||||
missing: true
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Save Search</label>
|
||||
<input ng-model="opts.savedSearch.title" input-focus class="form-control" placeholder="Name this search...">
|
||||
<input ng-model="opts.savedSearch.title" input-focus="select" class="form-control" placeholder="Name this search...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button ng-click="opts.saveDataSource()" ng-disabled="!opts.savedSearch.title" type="submit" class="btn btn-primary">
|
||||
|
|
|
@ -4,7 +4,7 @@ define(function (require) {
|
|||
$rootScope.globalState = globalState;
|
||||
|
||||
// and some local values
|
||||
$scope.appEmbedded = $location.search().embed;
|
||||
$scope.appEmbedded = $location.search().embed || false;
|
||||
$scope.httpActive = $http.pendingRequests;
|
||||
$scope.notifList = notify._notifs;
|
||||
|
||||
|
@ -13,4 +13,4 @@ define(function (require) {
|
|||
courier.start();
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<kbn-notifications list="notifList"></kbn-notifications>
|
||||
<div class="content" style="display: none;">
|
||||
<nav ng-hide="appEmbedded" bindonce class="navbar navbar-inverse navbar-static-top">
|
||||
<nav ng-class="{show: appEmbedded === false}" bindonce class="hide navbar navbar-inverse navbar-static-top">
|
||||
<div class="navbar-header">
|
||||
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
|
@ -51,4 +51,4 @@
|
|||
</config>
|
||||
|
||||
<div class="application" ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
</table>
|
||||
</p>
|
||||
<small>© 2014 All Rights Reserved - Elasticsearch</small>
|
||||
<small>© 2015 All Rights Reserved - Elasticsearch</small>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<label>Script <small>Please familiarize yourself with <a target="_window" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-script-fields.html#search-request-script-fields">script fields <i class="fa-link fa"></i></a> and with
|
||||
<a target="_window" href="http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-script">scripts in aggregations <i class="fa-link fa"></i></a>
|
||||
before using scripted fields.</small></label>
|
||||
<textarea required class="form-control span12" ng-model="scriptedField.script"></textarea>
|
||||
<textarea required class="scripted-field-script form-control span12" ng-model="scriptedField.script"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
<div ng-if="namingConflict" class="alert alert-danger">
|
||||
|
|
|
@ -149,6 +149,10 @@ kbn-settings-objects-view {
|
|||
.flex(4, 0, auto);
|
||||
}
|
||||
}
|
||||
|
||||
.scripted-field-script {
|
||||
font-family: @font-family-monospace;
|
||||
}
|
||||
}
|
||||
|
||||
kbn-settings-indices .fields {
|
||||
|
|
8
src/kibana/plugins/vis_types/vislib/editors/line.html
Normal file
8
src/kibana/plugins/vis_types/vislib/editors/line.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!-- vis type specific options -->
|
||||
<vislib-basic-options></vislib-basic-options>
|
||||
<div class="vis-option-item form-group">
|
||||
<label>
|
||||
<input type="checkbox" value="{{showCircles}}" ng-model="vis.params.showCircles" name="showCircles" ng-checked="vis.params.showCircles">
|
||||
Show Circles
|
||||
</label>
|
||||
</div>
|
|
@ -14,9 +14,10 @@ define(function (require) {
|
|||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
showCircles: true,
|
||||
defaultYExtents: false
|
||||
},
|
||||
editor: require('text!plugins/vis_types/vislib/editors/basic.html')
|
||||
editor: require('text!plugins/vis_types/vislib/editors/line.html')
|
||||
},
|
||||
schemas: new Schemas([
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<i class="fa fa-link"></i>
|
||||
|
||||
This visualization is linked to a saved search:
|
||||
<b>{{ savedVis.savedSearchId | json}}</b>
|
||||
<b>{{ savedVis.savedSearch.title }}</b>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<form role="form" ng-submit="conf.doSave()">
|
||||
<div class="form-group">
|
||||
<label for="visTitle">Title</label>
|
||||
<input class="form-control" input-focus type="text" name="visTitle" ng-model="conf.savedVis.title" required>
|
||||
<input class="form-control" input-focus="select" type="text" name="visTitle" ng-model="conf.savedVis.title" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</form>
|
|
@ -1,12 +1,12 @@
|
|||
<li class="sidebar-item" ng-show="vis.type.params.editor">
|
||||
<div class="sidebar-item-title" ng-click="showVisOptions = !showVisOptions">
|
||||
<div ng-hide="alwaysShowOptions" class="sidebar-item-title" ng-click="showVisOptions = !showVisOptions">
|
||||
view options
|
||||
<i
|
||||
class="fa fa-caret-down"
|
||||
ng-if="!alwaysShowOptions"
|
||||
ng-class="{'fa-caret-down': showVisOptions, 'fa-caret-right': !showVisOptions}">
|
||||
</i>
|
||||
</div>
|
||||
<div ng-show="alwaysShowOptions" class="sidebar-item-title">view options</div>
|
||||
|
||||
<div class="visualization-options" ng-show="alwaysShowOptions || showVisOptions"></div>
|
||||
</li>
|
||||
|
|
|
@ -276,6 +276,16 @@ notifications {
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
.kbn-timepicker .refresh-interval {
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: @border-radius-small;
|
||||
}
|
||||
|
||||
.kbn-timepicker .refresh-interval-active {
|
||||
background-color: @btn-info-bg;
|
||||
color: @btn-info-color;
|
||||
}
|
||||
|
||||
//== Table
|
||||
|
||||
kbn-table, .kbn-table {
|
||||
|
|
|
@ -18,6 +18,7 @@ program.option('-c, --config <path>', 'Path to the config file');
|
|||
program.option('-p, --port <port>', 'The port to bind to', parseInt);
|
||||
program.option('-q, --quiet', 'Turns off logging');
|
||||
program.option('-H, --host <host>', 'The host to bind to');
|
||||
program.option('-l, --log-file <path>', 'The file to log to');
|
||||
program.option('--plugins <path>', 'Path to scan for plugins');
|
||||
program.parse(process.argv);
|
||||
|
||||
|
@ -49,6 +50,10 @@ if (program.quiet) {
|
|||
config.quiet = program.quiet;
|
||||
}
|
||||
|
||||
if (program.logFile) {
|
||||
config.log_file = program.logFile;
|
||||
}
|
||||
|
||||
if (program.host) {
|
||||
config.host = program.host;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ var config = module.exports = {
|
|||
kibana : kibana,
|
||||
package : require(packagePath),
|
||||
htpasswd : htpasswdPath,
|
||||
buildNum : '@@buildNum'
|
||||
buildNum : '@@buildNum',
|
||||
log_file : kibana.log_file || null
|
||||
};
|
||||
|
||||
config.plugins = listPlugins(config);
|
||||
|
|
|
@ -49,3 +49,6 @@ verify_ssl: true
|
|||
# Set the path to where you would like the process id file to be created.
|
||||
# pid_file: /var/run/kibana.pid
|
||||
|
||||
# If you would like to send the log output to a file you can set the path below.
|
||||
# This will also turn off the STDOUT log output.
|
||||
# log_file: ./kibana.log
|
|
@ -69,8 +69,8 @@ function onListening() {
|
|||
}
|
||||
|
||||
function start() {
|
||||
var port = parseInt(process.env.PORT, 10) || config.port || 3000;
|
||||
var host = process.env.HOST || config.host || '127.0.0.1';
|
||||
var port = config.port || 3000;
|
||||
var host = config.host || '127.0.0.1';
|
||||
var listen = Promise.promisify(server.listen.bind(server));
|
||||
app.set('port', port);
|
||||
return listen(port, host);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
var _ = require('lodash');
|
||||
var Writable = require('stream').Writable;
|
||||
var util = require('util');
|
||||
var through = require('through');
|
||||
|
||||
var levels = {
|
||||
10: 'trace',
|
||||
|
@ -11,14 +10,7 @@ var levels = {
|
|||
60: 'fatal'
|
||||
};
|
||||
|
||||
function JSONStream(options) {
|
||||
options = options || {};
|
||||
Writable.call(this, options);
|
||||
}
|
||||
|
||||
util.inherits(JSONStream, Writable);
|
||||
|
||||
JSONStream.prototype._write = function (entry, encoding, callback) {
|
||||
function write(entry) {
|
||||
entry = JSON.parse(entry.toString('utf8'));
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
|
@ -36,8 +28,13 @@ JSONStream.prototype._write = function (entry, encoding, callback) {
|
|||
if (!output.message) output.message = output.error.message;
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify(output) + "\n");
|
||||
callback();
|
||||
};
|
||||
this.queue(JSON.stringify(output) + '\n');
|
||||
}
|
||||
|
||||
module.exports = JSONStream;
|
||||
function end() {
|
||||
this.queue(null);
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
return through(write, end);
|
||||
};
|
|
@ -2,18 +2,33 @@ var _ = require('lodash');
|
|||
var morgan = require('morgan');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
var bunyan = require('bunyan');
|
||||
var fs = require('fs');
|
||||
var StdOutStream = require('./StdOutStream');
|
||||
var JSONStream = require('./JSONStream');
|
||||
var createJSONStream = require('./createJSONStream');
|
||||
var config = require('../config');
|
||||
var stream = { stream: new JSONStream() };
|
||||
var streams = [];
|
||||
|
||||
// Set the default stream based on the enviroment. If we are on development then
|
||||
// then we are going to create a pretty stream. Everytyhing else will get the
|
||||
// JSON stream to stdout.
|
||||
var defaultStream;
|
||||
if (env === 'development') {
|
||||
stream.stream = new StdOutStream();
|
||||
defaultStream = new StdOutStream();
|
||||
} else {
|
||||
defaultStream = createJSONStream()
|
||||
.pipe(process.stdout);
|
||||
}
|
||||
|
||||
if (!config.quiet) {
|
||||
streams.push(stream);
|
||||
// If we are not being oppressed and we are not sending the output to a log file
|
||||
// push the default stream to the list of streams
|
||||
if (!config.quiet && !config.log_file) {
|
||||
streams.push({ stream: defaultStream });
|
||||
}
|
||||
|
||||
// Send the stream to a file using the json format.
|
||||
if (config.log_file) {
|
||||
var fileStream = fs.createWriteStream(config.log_file);
|
||||
streams.push({ stream: createJSONStream().pipe(fileStream) });
|
||||
}
|
||||
|
||||
var logger = module.exports = bunyan.createLogger({
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
var config = require('../config');
|
||||
var upgrade = require('./upgradeConfig');
|
||||
var client = require('./elasticsearch_client');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function (client) {
|
||||
var options = {
|
||||
index: config.kibana.kibana_index,
|
||||
type: 'config',
|
||||
|
@ -22,7 +21,7 @@ module.exports = function () {
|
|||
};
|
||||
|
||||
return client.search(options)
|
||||
.then(upgrade)
|
||||
.then(upgrade(client))
|
||||
.catch(function (err) {
|
||||
if (!/SearchParseException.+mapping.+\[buildNum\]|^IndexMissingException/.test(err.message)) throw err;
|
||||
});
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
var Promise = require('bluebird');
|
||||
var waitForEs = require('./waitForEs');
|
||||
var migrateConfig = require('./migrateConfig');
|
||||
var client = require('./elasticsearch_client');
|
||||
|
||||
module.exports = function () {
|
||||
return waitForEs().then(function () {
|
||||
return migrateConfig();
|
||||
return migrateConfig(client);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -3,29 +3,31 @@ var isUpgradeable = require('./isUpgradeable');
|
|||
var config = require('../config');
|
||||
var _ = require('lodash');
|
||||
var client = require('./elasticsearch_client');
|
||||
module.exports = function (response) {
|
||||
var newConfig = {};
|
||||
// Check to see if there are any doc. If not then we can assume
|
||||
// nothing needs to be done
|
||||
if (response.hits.hits.length === 0) return Promise.resolve();
|
||||
module.exports = function (client) {
|
||||
return function (response) {
|
||||
var newConfig = {};
|
||||
// Check to see if there are any doc. If not then we can assume
|
||||
// nothing needs to be done
|
||||
if (response.hits.hits.length === 0) return Promise.resolve();
|
||||
|
||||
// Look for upgradeable configs. If none of them are upgradeable
|
||||
// then resolve with null.
|
||||
var body = _.find(response.hits.hits, isUpgradeable);
|
||||
if (!body) return Promise.resolve();
|
||||
// if we already have a the current version in the index then we need to stop
|
||||
if (_.find(response.hits.hits, { _id: config.package.version })) return Promise.resolve();
|
||||
|
||||
// if the build number is still the template string (which it wil be in development)
|
||||
// then we need to set it to the max interger. Otherwise we will set it to the build num
|
||||
body._source.buildNum = (/^@@/.test(config.buildNum)) ? Math.pow(2, 53) - 1 : parseInt(config.buildNum, 10);
|
||||
// Look for upgradeable configs. If none of them are upgradeable
|
||||
// then resolve with null.
|
||||
var body = _.find(response.hits.hits, isUpgradeable);
|
||||
if (!body) return Promise.resolve();
|
||||
|
||||
return client.create({
|
||||
index: config.kibana.kibana_index,
|
||||
type: 'config',
|
||||
body: body._source,
|
||||
id: config.package.version
|
||||
}).catch(function (err) {
|
||||
// Ignore document already exists exceptions for beta and snapshot upgrades.
|
||||
if (/DocumentAlreadyExistsException/.test(err.message) && /beta|snapshot/.test(config.package.version)) return;
|
||||
throw err;
|
||||
});
|
||||
|
||||
// if the build number is still the template string (which it wil be in development)
|
||||
// then we need to set it to the max interger. Otherwise we will set it to the build num
|
||||
body._source.buildNum = (/^@@/.test(config.buildNum)) ? Math.pow(2, 53) - 1 : parseInt(config.buildNum, 10);
|
||||
|
||||
return client.create({
|
||||
index: config.kibana.kibana_index,
|
||||
type: 'config',
|
||||
body: body._source,
|
||||
id: config.package.version
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
25
test/unit/fixtures/config_upgrade_from_4.0.0.json
Normal file
25
test/unit/fixtures/config_upgrade_from_4.0.0.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"took": 1,
|
||||
"timed_out": false,
|
||||
"_shards": {
|
||||
"total": 1,
|
||||
"successful": 1,
|
||||
"failed": 0
|
||||
},
|
||||
"hits": {
|
||||
"total": 1,
|
||||
"max_score": 1,
|
||||
"hits": [
|
||||
{
|
||||
"_index": ".kibana",
|
||||
"_type": "config",
|
||||
"_id": "4.0.0",
|
||||
"_score": 1,
|
||||
"_source": {
|
||||
"buildNum": 5888,
|
||||
"defaultIndex": "logstash-*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"took": 1,
|
||||
"timed_out": false,
|
||||
"_shards": {
|
||||
"total": 1,
|
||||
"successful": 1,
|
||||
"failed": 0
|
||||
},
|
||||
"hits": {
|
||||
"total": 2,
|
||||
"max_score": 1,
|
||||
"hits": [
|
||||
{
|
||||
"_index": ".kibana",
|
||||
"_type": "config",
|
||||
"_id": "4.0.1-snapshot",
|
||||
"_score": 1,
|
||||
"_source": {
|
||||
"buildNum": 5921,
|
||||
"defaultIndex": "logstash-*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"_index": ".kibana",
|
||||
"_type": "config",
|
||||
"_id": "4.0.0",
|
||||
"_score": 1,
|
||||
"_source": {
|
||||
"buildNum": 5888,
|
||||
"defaultIndex": "logstash-*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
35
test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1.json
Normal file
35
test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"took": 1,
|
||||
"timed_out": false,
|
||||
"_shards": {
|
||||
"total": 1,
|
||||
"successful": 1,
|
||||
"failed": 0
|
||||
},
|
||||
"hits": {
|
||||
"total": 2,
|
||||
"max_score": 1,
|
||||
"hits": [
|
||||
{
|
||||
"_index": ".kibana",
|
||||
"_type": "config",
|
||||
"_id": "4.0.1",
|
||||
"_score": 1,
|
||||
"_source": {
|
||||
"buildNum": 5921,
|
||||
"defaultIndex": "logstash-*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"_index": ".kibana",
|
||||
"_type": "config",
|
||||
"_id": "4.0.0",
|
||||
"_score": 1,
|
||||
"_source": {
|
||||
"buildNum": 5888,
|
||||
"defaultIndex": "logstash-*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
75
test/unit/server/lib/upgradeConfig.js
Normal file
75
test/unit/server/lib/upgradeConfig.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
var root = require('requirefrom')('');
|
||||
var upgradeConfig = root('src/server/lib/upgradeConfig');
|
||||
var expect = require('expect.js');
|
||||
var sinon = require('sinon');
|
||||
var sinonAsPromised = require('sinon-as-promised')(require('bluebird'));
|
||||
var util = require('util');
|
||||
var package = root('package.json');
|
||||
var config = root('src/server/config');
|
||||
|
||||
var upgradeFrom4_0_0_to_4_0_1 = root('test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1.json');
|
||||
var upgradeFrom4_0_0_to_4_0_1_snapshot = root('test/unit/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json');
|
||||
var upgradeFrom4_0_0 = root('test/unit/fixtures/config_upgrade_from_4.0.0.json');
|
||||
|
||||
describe('lib/upgradeConfig', function () {
|
||||
|
||||
var client, oldPackageVersion, oldBuildNum;
|
||||
beforeEach(function () {
|
||||
oldPackageVersion = config.package.version;
|
||||
oldBuildNum = config.buildNum;
|
||||
client = { create: sinon.stub() };
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
config.package.version = oldPackageVersion;
|
||||
config.buildNum = oldBuildNum;
|
||||
});
|
||||
|
||||
it('should not upgrade if the current version of the config exits', function () {
|
||||
config.package.version = '4.0.1';
|
||||
var fn = upgradeConfig(client);
|
||||
client.create.rejects(new Error('DocumentAlreadyExistsException'));
|
||||
return fn(upgradeFrom4_0_0_to_4_0_1).finally(function () {
|
||||
sinon.assert.notCalled(client.create);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not upgrade if there are no hits', function () {
|
||||
config.package.version = '4.0.1';
|
||||
var fn = upgradeConfig(client);
|
||||
return fn({ hits: { hits: [] } }).finally(function () {
|
||||
sinon.assert.notCalled(client.create);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not upgrade even if a snapshot exists', function () {
|
||||
config.package.version = '4.0.1-snapshot';
|
||||
client.create.rejects(new Error('DocumentAlreadyExistsException'));
|
||||
var fn = upgradeConfig(client);
|
||||
return fn(upgradeFrom4_0_0_to_4_0_1_snapshot).finally(function () {
|
||||
sinon.assert.notCalled(client.create);
|
||||
});
|
||||
});
|
||||
|
||||
it('should upgrade from 4.0.0 to 4.0.1', function () {
|
||||
config.package.version = '4.0.1';
|
||||
config.buildNum = 5921;
|
||||
var fn = upgradeConfig(client);
|
||||
client.create.resolves({ _index: '.kibana', _type: 'config', _id: '4.0.1', _version: 1, created: true });
|
||||
return fn(upgradeFrom4_0_0).finally(function () {
|
||||
sinon.assert.calledOnce(client.create);
|
||||
var body = client.create.args[0][0];
|
||||
expect(body).to.eql({
|
||||
index: '.kibana',
|
||||
type: 'config',
|
||||
id: '4.0.1',
|
||||
body: {
|
||||
'buildNum': 5921,
|
||||
'defaultIndex': 'logstash-*'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -13,7 +13,8 @@ define(function (require) {
|
|||
'bytes': 100,
|
||||
'area': [{lat: 7, lon: 7}],
|
||||
'noMapping': 'hasNoMapping',
|
||||
'objectArray': [{foo: true}, {bar: false}]
|
||||
'objectArray': [{foo: true}, {bar: false}],
|
||||
'_underscore': 1
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -103,14 +104,23 @@ define(function (require) {
|
|||
});
|
||||
|
||||
describe('warnings', function () {
|
||||
it('displays a warning about field name starting with underscore', function () {
|
||||
var cells = $elem.find('td[title="_underscore"]').siblings();
|
||||
expect(cells.find('.doc-viewer-underscore').length).to.be(1);
|
||||
expect(cells.find('.doc-viewer-no-mapping').length).to.be(0);
|
||||
expect(cells.find('.doc-viewer-object-array').length).to.be(0);
|
||||
});
|
||||
|
||||
it('displays a warning about missing mappings', function () {
|
||||
var cells = $elem.find('td[title="noMapping"]').siblings();
|
||||
expect(cells.find('.doc-viewer-underscore').length).to.be(0);
|
||||
expect(cells.find('.doc-viewer-no-mapping').length).to.be(1);
|
||||
expect(cells.find('.doc-viewer-object-array').length).to.be(0);
|
||||
});
|
||||
|
||||
it('displays a warning about objects in arrays', function () {
|
||||
var cells = $elem.find('td[title="objectArray"]').siblings();
|
||||
expect(cells.find('.doc-viewer-underscore').length).to.be(0);
|
||||
expect(cells.find('.doc-viewer-no-mapping').length).to.be(0);
|
||||
expect(cells.find('.doc-viewer-object-array').length).to.be(1);
|
||||
});
|
||||
|
|
51
test/unit/specs/directives/input_focus.js
Normal file
51
test/unit/specs/directives/input_focus.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var $ = require('jquery');
|
||||
require('directives/input_focus');
|
||||
|
||||
describe('Input focus directive', function () {
|
||||
var $compile, $rootScope, $timeout, element;
|
||||
var $el, selectedEl, selectedText;
|
||||
var inputValue = 'Input Text Value';
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
|
||||
beforeEach(inject(function (_$compile_, _$rootScope_, _$timeout_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$timeout = _$timeout_;
|
||||
|
||||
$el = $('<div>');
|
||||
$el.appendTo('body');
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
$el.remove();
|
||||
$el = null;
|
||||
});
|
||||
|
||||
function renderEl(html) {
|
||||
$rootScope.value = inputValue;
|
||||
element = $compile(html)($rootScope);
|
||||
element.appendTo($el);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
selectedEl = document.activeElement;
|
||||
selectedText = window.getSelection().toString();
|
||||
}
|
||||
|
||||
|
||||
it('should focus the input', function () {
|
||||
renderEl('<input type="text" ng-model="value" input-focus />');
|
||||
expect(selectedEl).to.equal(element[0]);
|
||||
expect(selectedText.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should select the text in the input', function () {
|
||||
renderEl('<input type="text" ng-model="value" input-focus="select" />');
|
||||
expect(selectedEl).to.equal(element[0]);
|
||||
expect(selectedText.length).to.equal(inputValue.length);
|
||||
expect(selectedText).to.equal(inputValue);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -110,6 +110,14 @@ define(function (require) {
|
|||
done();
|
||||
});
|
||||
|
||||
it('should highlight the current active interval', function (done) {
|
||||
$scope.setRefreshInterval({ value: 300000 });
|
||||
$elem.scope().$digest();
|
||||
expect($elem.find('.refresh-interval-active').length).to.be(1);
|
||||
expect($elem.find('.refresh-interval-active').text().trim()).to.be('5 minutes');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should default the interval on the courier with incorrect values', function (done) {
|
||||
// Change refresh interval and digest
|
||||
$scope.setRefreshInterval('undefined');
|
||||
|
|
|
@ -64,7 +64,7 @@ define(function (require) {
|
|||
if (path.__data__.name === undefined) return false;
|
||||
return path.__data__.name.toString() === label;
|
||||
}).map(function (path) {
|
||||
return $(path).attr('class').split(/\s+/)[2].replace('c', '#');
|
||||
return $(path).attr('class').split(/\s+/)[1].replace('c', '#');
|
||||
});
|
||||
|
||||
slices.forEach(function (hex) {
|
||||
|
|
Loading…
Reference in a new issue