]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5820 Provide a permalink on each rule description
authorStas Vilchik <vilchiks@gmail.com>
Wed, 24 Dec 2014 16:18:45 +0000 (17:18 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 24 Dec 2014 16:18:45 +0000 (17:18 +0100)
13 files changed:
server/sonar-web/Gruntfile.coffee
server/sonar-web/src/main/coffee/issue/views/rule-overlay.coffee
server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-rule-meta.hbs
server/sonar-web/src/main/js/coding-rules/rule-details-view.js
server/sonar-web/src/main/js/coding-rules/rule/rule-meta-view.js
server/sonar-web/src/main/js/coding-rules/show-app.js [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/app.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/search.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/show.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-should-have-permalink.js [new file with mode: 0644]
server/sonar-web/src/main/less/components/rules.less
server/sonar-web/src/main/webapp/WEB-INF/app/controllers/coding_rules_controller.rb
server/sonar-web/src/main/webapp/WEB-INF/app/views/coding_rules/show.html.erb [new file with mode: 0644]

index 736f83885b8b984b5fa9963638a9035e659e6eea..4e00555ddfa9c44f4ae9175ff7f31177a59e2701 100644 (file)
@@ -174,6 +174,10 @@ module.exports = (grunt) ->
         name: 'coding-rules/app'
         out: '<%= pkg.assets %>build/js/coding-rules/app.js'
 
+      codingRulesShow: options:
+        name: 'coding-rules/app'
+        out: '<%= pkg.assets %>build/js/coding-rules/show-app.js'
+
       codingRulesOld: options:
         name: 'coding-rules-old/app'
         out: '<%= pkg.assets %>build/js/coding-rules-old/app.js'
index 78415e338e02d15f87376b73a9c5ba9c49759867..54d13bd8f0a9c7e20a6c9bde98e6976019765606 100644 (file)
@@ -12,5 +12,5 @@ define [
 
     serializeData: ->
       _.extend super,
-        permalink: "#{baseUrl}/coding_rules#rule_key=#{@model.get('key')}"
+        permalink: "#{baseUrl}/coding_rules/show?key=#{encodeURIComponent @model.get('key')}"
         allTags: _.union @model.get('sysTags'), @model.get('tags')
index 44cd94a7adfc268958186edcc297dba2fa517cac..383258b9bbb51073cc6b69a974611840c931e846 100644 (file)
@@ -1,6 +1,6 @@
 <h3 class="coding-rules-detail-header">
   {{name}}
-  <a class="coding-rules-detail-permalink icon-link" target="_blank" href="#rule_key={{key}}"></a>
+  <a class="coding-rules-detail-permalink icon-link" target="_blank" href="{{permalink}}"></a>
 </h3>
 
 <span class="subtitle">{{key}}</span>
index 2e41d7663fa03d42f3850aa15d852506095cab37..051413e679fc6ffb17a9a009706cf6098cc63382 100644 (file)
@@ -9,6 +9,7 @@ define([
 ], function (Backbone, Marionette, Templates, MetaView, DescView, ParamView, ProfilesView) {
 
   return Marionette.Layout.extend({
+    className: 'coding-rule-details',
     template: Templates['coding-rules-rule-details'],
 
     regions: {
index 6f3c3c367003a6e07baae04df1a12478c444d250..e01fa1d2942158274182fdff64e2813ca45bfb8f 100644 (file)
@@ -78,7 +78,8 @@ define([
       return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
         canWrite: this.options.app.canWrite,
         subCharacteristic: this.options.app.getSubCharacteristicName(this.model.get('debtSubChar')),
-        allTags: _.union(this.model.get('sysTags'), this.model.get('tags'))
+        allTags: _.union(this.model.get('sysTags'), this.model.get('tags')),
+        permalink: baseUrl + '/coding_rules/show?key=' + encodeURIComponent(this.model.id)
       });
     }
   });
diff --git a/server/sonar-web/src/main/js/coding-rules/show-app.js b/server/sonar-web/src/main/js/coding-rules/show-app.js
new file mode 100644 (file)
index 0000000..ee68beb
--- /dev/null
@@ -0,0 +1,94 @@
+requirejs.config({
+  baseUrl: baseUrl + '/js',
+
+  paths: {
+    'backbone': 'third-party/backbone',
+    'backbone.marionette': 'third-party/backbone.marionette',
+    'handlebars': 'third-party/handlebars'
+  },
+
+  shim: {
+    'backbone.marionette': {
+      deps: ['backbone'],
+      exports: 'Marionette'
+    },
+    'backbone': {
+      exports: 'Backbone'
+    },
+    'handlebars': {
+      exports: 'Handlebars'
+    }
+  }
+});
+
+
+requirejs([
+  'backbone',
+  'backbone.marionette',
+
+  'coding-rules/models/rule',
+  'coding-rules/rule-details-view',
+
+  'common/handlebars-extensions'
+],
+    function (Backbone,
+              Marionette,
+              Rule,
+              RuleDetailsView) {
+
+      var $ = jQuery,
+          App = new Marionette.Application(),
+          p = window.process.addBackgroundProcess();
+
+      App.addInitializer(function () {
+        var url = baseUrl + '/api/rules/show',
+            key = decodeURIComponent(window.location.search.substr(5)),
+            options = {
+              key: key,
+              actives: true
+            };
+        $.get(url, options).done(function (data) {
+          this.ruleDetailsView = new RuleDetailsView({
+            app: App,
+            model: new Rule(data.rule),
+            actives: data.actives
+          });
+          this.ruleDetailsView.render().$el.appendTo($('.page'));
+          window.process.finishBackgroundProcess(p);
+        }).fail(function () {
+          window.process.failBackgroundProcess(p);
+        });
+      });
+
+      App.manualRepository = function () {
+        return {
+          key: 'manual',
+          name: t('coding_rules.manual_rules'),
+          language: 'none'
+        };
+      };
+
+      App.getSubCharacteristicName = function (name) {
+        return (App.characteristics[name] || '').replace(': ', ' > ');
+      };
+
+      var appXHR = $.get(baseUrl + '/api/rules/app').done(function(r) {
+        App.canWrite = r.canWrite;
+        App.qualityProfiles = _.sortBy(r.qualityprofiles, ['name', 'lang']);
+        App.languages = _.extend(r.languages, {
+          none: 'None'
+        });
+        _.map(App.qualityProfiles, function(profile) {
+          profile.language = App.languages[profile.lang];
+        });
+        App.repositories = r.repositories;
+        App.repositories.push(App.manualRepository());
+        App.statuses = r.statuses;
+        App.characteristics = r.characteristics;
+      });
+
+      $.when(window.requestMessages(), appXHR).done(function () {
+        App.start();
+      });
+
+    });
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/app.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/app.json
new file mode 100644 (file)
index 0000000..4f3319c
--- /dev/null
@@ -0,0 +1,168 @@
+{
+  "canWrite": false,
+  "qualityprofiles": [
+    {
+      "key": "java-default-with-mojo-conventions-49307",
+      "name": "Default - Maven Conventions",
+      "lang": "java",
+      "parentKey": "java-top-profile-without-formatting-conventions-50037"
+    },
+    {
+      "key": "java-default-with-sonarsource-conventions-27339",
+      "name": "Default - SonarSource conventions",
+      "lang": "java",
+      "parentKey": "java-top-profile-without-formatting-conventions-50037"
+    },
+    {
+      "key": "java-top-profile-without-formatting-conventions-50037",
+      "name": "Default - Top",
+      "lang": "java"
+    },
+    {
+      "key": "java-findbugs-14954",
+      "name": "FindBugs",
+      "lang": "java"
+    },
+    {
+      "key": "java-for-sq-java-plugin-only-92289",
+      "name": "For SQ Java Plugin Only",
+      "lang": "java",
+      "parentKey": "java-default-with-sonarsource-conventions-27339"
+    },
+    {
+      "key": "java-for-sq-only-95381",
+      "name": "For SQ Only",
+      "lang": "java",
+      "parentKey": "java-default-with-sonarsource-conventions-27339"
+    },
+    {
+      "key": "php-psr-2-06315",
+      "name": "PSR-2",
+      "lang": "php"
+    },
+    {
+      "key": "java-sonar-way-80423",
+      "name": "Sonar way",
+      "lang": "java"
+    },
+    {
+      "key": "js-sonar-way",
+      "name": "Sonar way",
+      "lang": "js"
+    },
+    {
+      "key": "php-sonar-way-05548",
+      "name": "Sonar way",
+      "lang": "php"
+    },
+    {
+      "key": "py-sonar-way-80265",
+      "name": "Sonar way",
+      "lang": "py"
+    },
+    {
+      "key": "java-without-findbugs",
+      "name": "Without Findbugs",
+      "lang": "java"
+    }
+  ],
+  "languages": {
+    "py": "Python",
+    "js": "JavaScript",
+    "php": "PHP",
+    "java": "Java"
+  },
+  "repositories": [
+    {
+      "key": "common-java",
+      "name": "Common SonarQube",
+      "language": "java"
+    },
+    {
+      "key": "common-js",
+      "name": "Common SonarQube",
+      "language": "js"
+    },
+    {
+      "key": "common-php",
+      "name": "Common SonarQube",
+      "language": "php"
+    },
+    {
+      "key": "common-py",
+      "name": "Common SonarQube",
+      "language": "py"
+    },
+    {
+      "key": "Pylint",
+      "name": "Pylint",
+      "language": "py"
+    },
+    {
+      "key": "javascript",
+      "name": "SonarQube",
+      "language": "js"
+    },
+    {
+      "key": "php",
+      "name": "SonarQube",
+      "language": "php"
+    },
+    {
+      "key": "python",
+      "name": "SonarQube",
+      "language": "py"
+    },
+    {
+      "key": "squid",
+      "name": "SonarQube",
+      "language": "java"
+    }
+  ],
+  "statuses": {
+    "BETA": "Beta",
+    "DEPRECATED": "Deprecated",
+    "READY": "Ready"
+  },
+  "characteristics": {
+    "UNDERSTANDABILITY": "Maintainability: Understandability",
+    "MAINTAINABILITY": "Maintainability",
+    "TIME_ZONE_RELATED_PORTABILITY": "Portability: Time zone related portability",
+    "READABILITY": "Maintainability: Readability",
+    "SECURITY_FEATURES": "Security: Security features",
+    "ARCHITECTURE_RELIABILITY": "Reliability: Architecture related reliability",
+    "OS_RELATED_PORTABILITY": "Portability: OS related portability",
+    "EXCEPTION_HANDLING": "Reliability: Exception handling",
+    "LOGIC_CHANGEABILITY": "Changeability: Logic related changeability",
+    "SOFTWARE_RELATED_PORTABILITY": "Portability: Software related portability",
+    "INPUT_VALIDATION_AND_REPRESENTATION": "Security: Input validation and representation",
+    "LANGUAGE_RELATED_PORTABILITY": "Portability: Language related portability",
+    "ERRORS": "Security: Errors",
+    "SECURITY": "Security",
+    "RELIABILITY": "Reliability",
+    "PORTABILITY": "Portability",
+    "HARDWARE_RELATED_PORTABILITY": "Portability: Hardware related portability",
+    "SYNCHRONIZATION_RELIABILITY": "Reliability: Synchronization related reliability",
+    "TRANSPORTABILITY": "Reusability: Transportability",
+    "COMPILER_RELATED_PORTABILITY": "Portability: Compiler related portability",
+    "RESOURCE_RELIABILITY": "Reliability: Resource",
+    "CPU_EFFICIENCY": "Efficiency: Processor use",
+    "EFFICIENCY": "Efficiency",
+    "CHANGEABILITY": "Changeability",
+    "DATA_CHANGEABILITY": "Changeability: Data related changeability",
+    "API_ABUSE": "Security: API abuse",
+    "ARCHITECTURE_CHANGEABILITY": "Changeability: Architecture related changeability",
+    "UNIT_TESTS": "Reliability: Unit tests",
+    "INSTRUCTION_RELIABILITY": "Reliability: Instruction related reliability",
+    "REUSABILITY": "Reusability",
+    "MODULARITY": "Reusability: Modularity",
+    "UNIT_TESTABILITY": "Testability: Unit level testability",
+    "TESTABILITY": "Testability",
+    "INTEGRATION_TESTABILITY": "Testability: Integration level testability",
+    "NETWORK_USE": "Efficiency: Network use",
+    "MEMORY_EFFICIENCY": "Efficiency: Memory use",
+    "DATA_RELIABILITY": "Reliability: Data related reliability",
+    "FAULT_TOLERANCE": "Reliability: Fault tolerance",
+    "LOGIC_RELIABILITY": "Reliability: Logic related reliability"
+  }
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/search.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/search.json
new file mode 100644 (file)
index 0000000..b5a6bc2
--- /dev/null
@@ -0,0 +1,171 @@
+{
+  "total": 10,
+  "p": 1,
+  "ps": 200,
+  "rules": [
+    {
+      "key": "squid:S1181",
+      "name": "Throwable and Error classes should not be caught",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "error-handling"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1849",
+      "name": "\"Iterator.hasNext()\" should not call \"Iterator.next()\"",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1844",
+      "name": "\"Object.wait(...)\" should never be called on objects that implement \"java.util.concurrent.locks.Condition\"",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug",
+        "pitfall"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S2258",
+      "name": "\"javax.crypto.NullCipher\" should not be used for anything other than testing",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "cwe",
+        "owasp-top10",
+        "security"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S2251",
+      "name": "A \"for\" loop update clause should move the counter in the right direction",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:ObjectFinalizeOverridenCallsSuperFinalizeCheck",
+      "name": "super.finalize() should be called at the end of Object.finalize() implementations",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1143",
+      "name": "Return statements should not occur in finally blocks",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1206",
+      "name": "\"equals(Object obj)\" and \"hashCode()\" should be overridden in pairs",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1451",
+      "name": "Copyright and license headers should be defined in all source files",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "convention"
+      ],
+      "tags": []
+    },
+    {
+      "key": "squid:S1697",
+      "name": "Short-circuit logic should be used to prevent null pointer dereferences in conditionals",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [
+        "bug"
+      ],
+      "tags": []
+    }
+  ],
+  "facets": [
+    {
+      "property": "tags",
+      "values": [
+        {
+          "val": "bug",
+          "count": 7
+        },
+        {
+          "val": "convention",
+          "count": 1
+        },
+        {
+          "val": "cwe",
+          "count": 1
+        },
+        {
+          "val": "error-handling",
+          "count": 1
+        },
+        {
+          "val": "owasp-top10",
+          "count": 1
+        },
+        {
+          "val": "pitfall",
+          "count": 1
+        },
+        {
+          "val": "security",
+          "count": 1
+        }
+      ]
+    },
+    {
+      "property": "languages",
+      "values": [
+        {
+          "val": "java",
+          "count": 10
+        },
+        {
+          "val": "js",
+          "count": 6
+        },
+        {
+          "val": "php",
+          "count": 2
+        }
+      ]
+    },
+    {
+      "property": "repositories",
+      "values": [
+        {
+          "val": "squid",
+          "count": 10
+        }
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/show.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-permalink/show.json
new file mode 100644 (file)
index 0000000..274b587
--- /dev/null
@@ -0,0 +1,76 @@
+{
+  "rule": {
+    "key": "squid:S1181",
+    "repo": "squid",
+    "name": "Throwable and Error classes should not be caught",
+    "createdAt": "2013-08-09T14:40:54+0200",
+    "severity": "BLOCKER",
+    "status": "READY",
+    "internalKey": "S1181",
+    "isTemplate": false,
+    "tags": [],
+    "sysTags": [
+      "error-handling"
+    ],
+    "lang": "java",
+    "langName": "Java",
+    "htmlDesc": "<p>\n<code>Throwable</code> is the superclass of all errors and exceptions in Java.\n<code>Error</code> is the superclass of all errors which are not meant to be caught by applications.\n</p>\n\n<p>\nCatching either <code>Throwable</code> or <code>Error</code> will also catch <code>OutOfMemoryError</code> or <code>InternalError</code> from which an application should not attempt to recover.\n</p>\n\n<p>Only <code>Exception</code> and its subclasses should be caught.</p>\n\n<h2>Noncompliant Code Example</h2>\n\n<pre>\ntry { /* ... */ } catch (Throwable t) { /* ... */ }\ntry { /* ... */ } catch (Error e) { /* ... */ } \n</pre>\n\n<h2>Compliant Solution</h2>\n\n<pre>\ntry { /* ... */ } catch (Exception e) { /* ... */ }  \ntry { /* ... */ } catch (RuntimeException e) { /* ... */ }  \ntry { /* ... */ } catch (MyException e) { /* ... */ }  \n</pre>",
+    "defaultDebtChar": "RELIABILITY",
+    "defaultDebtSubChar": "EXCEPTION_HANDLING",
+    "debtChar": "RELIABILITY",
+    "debtSubChar": "EXCEPTION_HANDLING",
+    "debtCharName": "Reliability",
+    "debtSubCharName": "Exception handling",
+    "defaultDebtRemFnType": "CONSTANT_ISSUE",
+    "defaultDebtRemFnOffset": "20min",
+    "debtOverloaded": true,
+    "debtRemFnType": "LINEAR",
+    "debtRemFnCoeff": "20min",
+    "params": [
+      {
+        "key": "max",
+        "htmlDesc": "Maximum authorized number of parameters",
+        "type": "INTEGER",
+        "defaultValue": "7"
+      }
+    ]
+  },
+  "actives": [
+    {
+      "qProfile": "java-top-profile-without-formatting-conventions-50037",
+      "inherit": "NONE",
+      "severity": "BLOCKER",
+      "params": []
+    },
+    {
+      "qProfile": "java-default-with-sonarsource-conventions-27339",
+      "inherit": "INHERITED",
+      "severity": "BLOCKER",
+      "params": []
+    },
+    {
+      "qProfile": "java-for-sq-java-plugin-only-92289",
+      "inherit": "INHERITED",
+      "severity": "BLOCKER",
+      "params": []
+    },
+    {
+      "qProfile": "java-for-sq-only-95381",
+      "inherit": "INHERITED",
+      "severity": "BLOCKER",
+      "params": []
+    },
+    {
+      "qProfile": "java-default-with-mojo-conventions-49307",
+      "inherit": "INHERITED",
+      "severity": "BLOCKER",
+      "params": []
+    },
+    {
+      "qProfile": "java-sonar-way-80423",
+      "inherit": "NONE",
+      "severity": "BLOCKER",
+      "params": []
+    }
+  ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-should-have-permalink.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-rule-should-have-permalink.js
new file mode 100644 (file)
index 0000000..75898fd
--- /dev/null
@@ -0,0 +1,36 @@
+/* global casper:false */
+
+var lib = require('../lib');
+
+lib.initMessages();
+lib.changeWorkingDirectory('coding-rules-page-rule-permalink');
+
+
+casper.test.begin('coding-rules-page-rule-permalink', 1, function (test) {
+  casper
+      .start(lib.buildUrl('coding-rules'), function () {
+        lib.setDefaultViewport();
+
+        lib.mockRequest('/api/l10n/index', '{}');
+        lib.mockRequestFromFile('/api/rules/app', 'app.json');
+        lib.mockRequestFromFile('/api/rules/search', 'search.json');
+        lib.mockRequestFromFile('/api/rules/show', 'show.json');
+      })
+
+      .then(function () {
+        casper.waitForSelector('.coding-rule.selected');
+      })
+
+      .then(function () {
+        casper.click('.coding-rule.selected .js-rule');
+        casper.waitForSelector('.coding-rules-detail-header');
+      })
+
+      .then(function () {
+        test.assertExists('a[href="/coding_rules/show?key=squid%3AS1181"]');
+      })
+
+      .run(function () {
+        test.done();
+      });
+});
index affe5c40766ec0f89b00a73d323f2f3cdb549295..41793326f449a6b006271142c665e73c21497cc9 100644 (file)
@@ -57,3 +57,7 @@
     content: "";
   }
 }
+
+.coding-rule-details {
+  max-width: 1020px;
+}
index cc4298b93830e0ee8f40239829772b5bfce4db3c..04900e82a391a086b14c7e8daa71835e3485a4e6 100644 (file)
@@ -27,4 +27,9 @@ class CodingRulesController < ApplicationController
 
   end
 
+  # GET /coding_rules/show
+  def show
+
+  end
+
 end
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/coding_rules/show.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/coding_rules/show.html.erb
new file mode 100644 (file)
index 0000000..629a4a1
--- /dev/null
@@ -0,0 +1,6 @@
+<% content_for :script do %>
+  <script data-main="<%= ApplicationController.root_context -%>/js/coding-rules/show-app" src="<%= ApplicationController.root_context -%>/js/require.js"></script>
+<% end %>
+
+
+<div class="page"></div>