Procházet zdrojové kódy

added lexer/parser support for templated metric expressions, karma test runner is working

Torkel Ödegaard před 12 roky
rodič
revize
cef4349b2a

+ 12 - 1
src/app/controllers/graphiteTarget.js

@@ -82,13 +82,24 @@ function (angular, _, config, graphiteFuncs, Parser) {
 
 
         $scope.segments = _.map(astNode.segments, function(segment) {
         $scope.segments = _.map(astNode.segments, function(segment) {
           return {
           return {
+            type: segment.type,
             val: segment.value,
             val: segment.value,
-            html: segment.value === '*' ? '<i class="icon-asterisk"><i>' : segment.value
+            html: getSegmentHtml(segment)
           };
           };
         });
         });
       }
       }
     }
     }
 
 
+    function getSegmentHtml(segment) {
+      if (segment.value === '*') {
+        return '<i class="icon-asterisk"><i>';
+      }
+      if (segment.type === 'template') {
+        return "<span style='color: #ECEC09'>[[" + segment.value  + "]]</span>";
+      }
+      return segment.value;
+    }
+
     function getSegmentPathUpTo(index) {
     function getSegmentPathUpTo(index) {
       var arr = $scope.segments.slice(0, index);
       var arr = $scope.segments.slice(0, index);
 
 

+ 21 - 0
src/app/services/graphite/lexer.js

@@ -182,6 +182,7 @@ define([
 
 
       match =
       match =
         this.scanIdentifier() ||
         this.scanIdentifier() ||
+        this.scanTemplateSequence() ||
         this.scanPunctuator() ||
         this.scanPunctuator() ||
         this.scanNumericLiteral();
         this.scanNumericLiteral();
 
 
@@ -194,6 +195,26 @@ define([
       return null;
       return null;
     },
     },
 
 
+    scanTemplateSequence: function() {
+      if (this.peek() === '[' && this.peek(1) === '[') {
+        return {
+          type: 'templateStart',
+          value: '[[',
+          pos: this.char
+        };
+      }
+
+      if (this.peek() === ']' && this.peek(1) === ']') {
+        return {
+          type: 'templateEnd',
+          value: '[[',
+          pos: this.char
+        };
+      }
+
+      return null;
+    },
+
       /*
       /*
      * Extract a JavaScript identifier out of the next sequence of
      * Extract a JavaScript identifier out of the next sequence of
      * characters or return 'null' if its not possible. In addition,
      * characters or return 'null' if its not possible. In addition,

+ 42 - 10
src/app/services/graphite/parser.js

@@ -29,29 +29,61 @@ define([
       }
       }
     },
     },
 
 
-    metricExpression: function() {
+    metricSegment: function() {
+      if (this.match('identifier')) {
+        this.index++;
+        return {
+          type: 'segment',
+          value: this.tokens[this.index-1].value
+        };
+      }
+
+      if (!this.match('templateStart')) {
+        this.errorMark('Expected metric identifier');
+      }
+
+      this.index++;
+
       if (!this.match('identifier')) {
       if (!this.match('identifier')) {
+        this.errorMark('Expected identifier after templateStart');
+      }
+
+      var node = {
+        type: 'template',
+        value: this.tokens[this.index].value
+      };
+
+      this.index++;
+
+      if (!this.match('templateEnd')) {
+        this.errorMark('Expected templateEnd');
+      }
+
+      this.index++;
+      return node;
+    },
+
+    metricExpression: function() {
+      if (!this.match('templateStart') && !this.match('identifier')) {
         return null;
         return null;
       }
       }
 
 
       var node = {
       var node = {
         type: 'metric',
         type: 'metric',
-        segments: [{
-          type: 'segment',
-          value: this.tokens[this.index].value
-        }]
+        segments: []
       };
       };
 
 
-      this.index++;
+      node.segments.push(this.metricSegment());
 
 
-      if (this.match('.')) {
+      while(this.match('.')) {
         this.index++;
         this.index++;
-        var rest = this.metricExpression();
-        if (!rest) {
+
+        var segment = this.metricSegment();
+        if (!segment) {
           this.errorMark('Expected metric identifier');
           this.errorMark('Expected metric identifier');
         }
         }
 
 
-        node.segments = node.segments.concat(rest.segments);
+        node.segments.push(segment);
       }
       }
 
 
       return node;
       return node;

+ 27 - 0
src/test/karma.conf.js

@@ -0,0 +1,27 @@
+module.exports = function(config) {
+  config.set({
+    basePath: '../',
+
+    frameworks: ['mocha', 'requirejs', 'expect'],
+
+    // list of files / patterns to load in the browser
+    files: [
+      'test/test-main.js',
+      {pattern: 'app/**/*.js', included: false},
+      {pattern: 'test/**/*.js', included: false}
+    ],
+
+    // list of files to exclude
+    exclude: [
+    ],
+
+    reporters: ['progress'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome'],
+    captureTimeout: 60000,
+    singleRun: false
+  });
+};

+ 9 - 0
src/test/specs/lexer-specs.js

@@ -30,6 +30,15 @@ define([
       expect(tokens[tokens.length - 1].value).to.be(')');
       expect(tokens[tokens.length - 1].value).to.be(')');
     });
     });
 
 
+    it('should tokenize metric with template parameter', function() {
+      var lexer = new Lexer("metric.[[server]].test");
+      var tokens = lexer.tokenize();
+      expect(tokens[2].type).to.be('templateStart');
+      expect(tokens[3].type).to.be('identifier');
+      expect(tokens[3].value).to.be('server');
+      expect(tokens[4].type).to.be('templateEnd');
+    });
+
     it('should handle error with unterminated string', function() {
     it('should handle error with unterminated string', function() {
       var lexer = new Lexer("alias(metric, 'asd)");
       var lexer = new Lexer("alias(metric, 'asd)");
       var tokens = lexer.tokenize();
       var tokens = lexer.tokenize();

+ 9 - 0
src/test/specs/parser-specs.js

@@ -54,6 +54,15 @@ define([
       expect(rootNode.params[1].type).to.be('metric');
       expect(rootNode.params[1].type).to.be('metric');
     });
     });
 
 
+    it('function with templated series', function() {
+      var parser = new Parser("sum(test.[[server]].count)");
+      var rootNode = parser.getAst();
+
+      expect(rootNode.message).to.be(undefined)
+      expect(rootNode.params[0].type).to.be('metric');
+      expect(rootNode.params[0].segments[1].type).to.be('template');
+    });
+
     it('invalid metric expression', function() {
     it('invalid metric expression', function() {
       var parser = new Parser('metric.test.*.asd.');
       var parser = new Parser('metric.test.*.asd.');
       var rootNode = parser.getAst();
       var rootNode = parser.getAst();

+ 10 - 0
src/test/test-main.js

@@ -0,0 +1,10 @@
+require.config({
+    baseUrl:'base'
+});
+
+require([
+  'test/specs/lexer-specs',
+  'test/specs/parser-specs',
+], function () {
+  window.__karma__.start();
+});

+ 9 - 0
tasks/options/karma.js

@@ -0,0 +1,9 @@
+module.exports = function(config) {
+  return {
+    unit: {
+      configFile: 'src/test/karma.conf.js',
+      singleRun: false,
+      browsers: ['Chrome']
+    }
+  };
+};