diff --git a/src/kibana/components/config/defaults.js b/src/kibana/components/config/defaults.js index 1af0fd1a15fd..b64fae5c7d6d 100644 --- a/src/kibana/components/config/defaults.js +++ b/src/kibana/components/config/defaults.js @@ -79,6 +79,42 @@ define(function (require) { 'truncate:maxHeight': { value: 115, description: 'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation.' + }, + 'deafultFormat:ip': { + value: 'string', + description: 'The default format to be used for fields of type ip.' + }, + 'deafultFormat:date': { + value: 'string', + description: 'The default format to be used for fields of type date.' + }, + 'deafultFormat:string': { + value: 'string', + description: 'The default format to be used for fields of type string.' + }, + 'deafultFormat:number': { + value: 'string', + description: 'The default format to be used for fields of type number.' + }, + 'deafultFormat:boolean': { + value: 'string', + description: 'The default format to be used for fields of type boolean.' + }, + 'deafultFormat:conflict': { + value: 'string', + description: 'The default format to be used for fields of type conflict.' + }, + 'deafultFormat:geo_point': { + value: 'string', + description: 'The default format to be used for fields of type geo_point.' + }, + 'deafultFormat:geo_shape': { + value: 'string', + description: 'The default format to be used for fields of type geo_shape.' + }, + 'deafultFormat:attachment': { + value: 'string', + description: 'The default format to be used for fields of type attachment.' } }; }); \ No newline at end of file diff --git a/src/kibana/components/index_patterns/_field_formats.js b/src/kibana/components/index_patterns/_field_formats.js deleted file mode 100644 index 0b4efed93843..000000000000 --- a/src/kibana/components/index_patterns/_field_formats.js +++ /dev/null @@ -1,175 +0,0 @@ -/** - - ### Formatting a value - To format a response value, you need to get ahold of the field list, which is usually available at `indexPattern.fields`. Each field object has a `format` property*, which is an object detailed in [_field_formats.js](https://github.com/elastic/kibana4/blob/master/src/kibana/components/index_patterns/_field_formats.js). - - Once you have the field that a response value came from, pass the value to `field.format.convert(value)` and a formatted string representation of the field will be returned. - - \* the `format` property on field object's is a non-enumerable getter, meaning that if you itterate/clone/stringify the field object the format property will not be present. - - ### Changing a field's format - - Currently only one field format exists, `"string"`, which just [flattens any value down to a string](https://github.com/elastic/kibana4/blob/master/src/kibana/components/index_patterns/_field_formats.js#L18-L24). - - To change the format for a specific field you can either change the default for a field type modify the [default format mapping here](https://github.com/elastic/kibana4/blob/master/src/kibana/components/index_patterns/_field_formats.js#L37-L46). - - To change the format for a specific indexPattern's field, add the field and format name to `indexPattern.customFormats` object property. - - ```js - $scope.onChangeFormat = function (field, format) { - indexPattern.customFormats[field.name] = format.name; - }; - ``` - - ### Passing the formats to a chart - Currently, the [histogram formatter](https://github.com/elastic/kibana4/blob/master/src/plugins/visualize/saved_visualizations/resp_converters/histogram.js) passes the formatting function as the `xAxisFormatter` and `yAxisFormatter` function. - -*/ - -define(function (require) { - return function FieldFormattingService($rootScope, config) { - var _ = require('lodash'); - var angular = require('angular'); - var moment = require('moment'); - - function stringConverter(val) { - return formatField(val, function (val) { - if (_.isObject(val)) { - return angular.toJson(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }); - } - - var formats = [ - { - types: [ - 'number', - 'boolean', - 'date', - 'ip', - 'attachment', - 'geo_point', - 'geo_shape', - 'string', - 'conflict' - ], - name: 'string', - convert: stringConverter - }, - { - types: [ - 'date' - ], - name: 'date', - convert: function (val) { - return formatField(val, function (val) { - if (_.isNumber(val) || _.isDate(val)) { - return moment(val).format(config.get('dateFormat')); - } else { - return val; - } - }); - } - }, - { - types: [ - 'ip' - ], - name: 'ip', - convert: function (val) { - return formatField(val, function (val) { - if (!isFinite(val)) return val; - return [val >>> 24, val >>> 16 & 0xFF, val >>> 8 & 0xFF, val & 0xFF].join('.'); - }); - } - }, - { - types: [ - 'number' - ], - name: 'kilobytes', - convert: function (val) { - return formatField(val, function (val) { - return (val / 1024).toFixed(config.get('format:numberPrecision')) + ' kb'; - }); - } - }, - { - types: [ - 'number', - 'murmur3' - ], - name: 'number', - convert: function (val) { - return formatField(val, function (val) { - if (_.isNumber(val)) { - return +val.toFixed(config.get('format:numberPrecision')); - } else { - return stringConverter(val); - } - }); - } - } - ]; - - function formatField(value, fn) { - if (_.isArray(value)) { - if (value.length === 1) { - return fn(value[0]); - } else { - return angular.toJson(_.map(value, fn)); - } - } else { - return fn(value); - } - } - - formats.byType = _.transform(formats, function (byType, formatter) { - formatter.types.forEach(function (type) { - var list = byType[type] || (byType[type] = []); - list.push(formatter); - }); - }, {}); - - formats.byName = _.indexBy(formats, 'name'); - - formats.defaultByType = { - number: formats.byName.number, - murmur3: formats.byName.number, - date: formats.byName.date, - boolean: formats.byName.string, - ip: formats.byName.ip, - attachment: formats.byName.string, - geo_point: formats.byName.string, - geo_shape: formats.byName.string, - string: formats.byName.string, - conflict: formats.byName.string - }; - - /** - * Wrap the dateFormat.convert function in memoize, - * as moment is a huge performance issue if not memoized. - * - * @return {void} - */ - function memoizeDateFormat() { - var format = formats.byName.date; - if (!format._origConvert) { - format._origConvert = format.convert; - } - format.convert = _.memoize(format._origConvert); - } - - // memoize once config is ready, and every time the date format changes - $rootScope.$on('init:config', memoizeDateFormat); - $rootScope.$on('change:config.dateFormat', memoizeDateFormat); - - return formats; - }; -}); diff --git a/src/kibana/components/index_patterns/index_patterns.js b/src/kibana/components/index_patterns/index_patterns.js index facad694a406..ee48459cc57a 100644 --- a/src/kibana/components/index_patterns/index_patterns.js +++ b/src/kibana/components/index_patterns/index_patterns.js @@ -42,7 +42,7 @@ define(function (require) { self.intervals = Private(require('components/index_patterns/_intervals')); self.mapper = Private(require('components/index_patterns/_mapper')); self.patternToWildcard = Private(require('components/index_patterns/_pattern_to_wildcard')); - self.fieldFormats = Private(require('components/index_patterns/_field_formats')); + self.fieldFormats = Private(require('registry/field_formats')); self.IndexPattern = IndexPattern; }); }); diff --git a/src/kibana/components/vis/_agg_config.js b/src/kibana/components/vis/_agg_config.js index 8f412283b64a..09bd04857ee2 100644 --- a/src/kibana/components/vis/_agg_config.js +++ b/src/kibana/components/vis/_agg_config.js @@ -1,7 +1,7 @@ define(function (require) { return function AggConfigFactory(Private, fieldTypeFilter) { var _ = require('lodash'); - var fieldFormats = Private(require('components/index_patterns/_field_formats')); + var fieldFormats = Private(require('registry/field_formats')); function AggConfig(vis, opts) { var self = this; diff --git a/src/kibana/registry/field_formats.js b/src/kibana/registry/field_formats.js new file mode 100644 index 000000000000..50449b3e3d79 --- /dev/null +++ b/src/kibana/registry/field_formats.js @@ -0,0 +1,16 @@ +define(function (require) { + var _ = require('lodash'); + + return require('registry/_registry')({ + name: 'fieldFormats', + index: ['name'], + group: ['compatability'], + + constructor: function (config) { + this.defaultForType = function (type) { + var name = config.get('deafultFormat:' + type); + return this.byName[name] || _.asString; + }; + } + }); +}); \ No newline at end of file diff --git a/test/unit/specs/components/index_pattern/_field_formats.js b/test/unit/specs/components/index_pattern/_field_formats.js deleted file mode 100644 index f53e8d9f04a0..000000000000 --- a/test/unit/specs/components/index_pattern/_field_formats.js +++ /dev/null @@ -1,151 +0,0 @@ -define(function (require) { - return ['Field Formatters', function () { - var _ = require('lodash'); - var moment = require('moment'); - - var _config; - var formatters; - var formatter; - var types = [ - 'number', - 'boolean', - 'date', - 'ip', - 'attachment', - 'geo_point', - 'geo_shape', - 'murmur3', - 'string', - 'conflict' - ]; - - function formatFn(typeOrName) { - return (formatters.byName[typeOrName] || formatters.defaultByType[typeOrName]).convert; - } - - beforeEach(module('kibana')); - beforeEach(inject(function (Private, $injector, config) { - _config = config; - formatters = Private(require('components/index_patterns/_field_formats')); - })); - - it('should be an Object', function () { - expect(formatters).to.be.an(Object); - }); - - it('should have formatters indexed by type and by name', function () { - expect(formatters.byType).to.be.an(Object); - expect(formatters.byName).to.be.an(Object); - }); - - it('should have 1 or more formatters for each of ' + types.join(','), function () { - _.each(types, function (type) { - expect(formatters.byType[type]).to.be.an(Array); - _.each(formatters.byType[type], function (formatter) { - expect(formatter.convert).to.be.a(Function); - }); - }); - }); - - it('should expose default formatters for each type', function () { - _.each(types, function (type) { - expect(formatters.defaultByType[type]).to.be.an(Object); - }); - }); - - describe('Array handling', function () { - - it('should unwrap single item arrays', function () { - formatter = formatFn('string'); - expect(formatter(['foo'])).to.not.be.an(Array); - expect(formatter(['foo'])).to.be('foo'); - }); - - it('should stringify arrays longer than 1 element', function () { - formatter = formatFn('ip'); - expect(formatter([0, 2130706433])).to.not.be.an(Array); - expect(formatter([0, 2130706433])).to.be('["0.0.0.0","127.0.0.1"]'); - }); - }); - - - describe('string formatter', function () { - - beforeEach(function () { - formatter = formatFn('string'); - }); - - it('should the string value of the field', function () { - expect(formatter('foo')).to.be('foo'); - expect(formatter(5)).to.be('5'); - }); - - it('should return JSON for objects', function () { - expect(formatter({foo: true})).to.be('{"foo":true}'); - }); - - it('should return an empty string for null', function () { - expect(formatter(null)).to.be(''); - }); - - }); - - describe('date formatter', function () { - - var dateFormat = 'YYYY-MM-DD'; - beforeEach(function () { - _config.set('dateFormat', dateFormat); - formatter = formatFn('date'); - }); - - it('should format numbers', function () { - expect(formatter(0)).to.be(moment(0).format(dateFormat)); - }); - - it('should format dates', function () { - expect(formatter(new Date(0))).to.be(moment(0).format(dateFormat)); - }); - - it('should not format strings', function () { - expect(formatter('2014-11')).to.be('2014-11'); - }); - - }); - - - describe('ip formatter', function () { - - beforeEach(function () { - formatter = formatFn('ip'); - }); - - it('should format numbers', function () { - expect(formatter(2130706433)).to.be('127.0.0.1'); - }); - - it('should coerce numbers that are strings', function () { - expect(formatter('2130706433')).to.be('127.0.0.1'); - }); - - it('should not coerce strings that are not numbers', function () { - expect(formatter('foo')).to.be('foo'); - }); - - }); - - describe('kilobyte formatter', function () { - beforeEach(function () { - formatter = formatFn('kilobytes'); - }); - - it('should be a function', function () { - expect(formatter).to.be.a(Function); - }); - - it('should format a number as kilobytes', function () { - expect(formatter(1024)).to.be('1.000 kb'); - }); - }); - - }]; -}); \ No newline at end of file