aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-02-25 19:08:22 +0100
committerStas Vilchik <vilchiks@gmail.com>2016-02-29 14:13:36 +0100
commit7e1bd1a88c7b36d2dd7a678423705d2efddb5d83 (patch)
tree46c67aee74a39a735faeb7324dbf811bbc749db7 /server/sonar-web/src/main/js/apps
parent9c4e7a049ce145694c76307dcb6868be4fd9e8d2 (diff)
downloadsonarqube-7e1bd1a88c7b36d2dd7a678423705d2efddb5d83.tar.gz
sonarqube-7e1bd1a88c7b36d2dd7a678423705d2efddb5d83.zip
SONAR-7074 SONAR-7310 SONAR-7357 SONAR-7395 refactor web api page
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/action-view.js70
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/actions-view.js58
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/app.js89
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/controller.js99
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/filters-view.js46
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/item-view.js70
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/layout.js41
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/search-view.js53
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs90
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs6
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs12
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-search.hbs4
-rw-r--r--server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs7
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/app.js (renamed from server/sonar-web/src/main/js/apps/api-documentation/list.js)38
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Action.js117
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.js (renamed from server/sonar-web/src/main/js/apps/api-documentation/list-view.js)29
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Domain.js66
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.js (renamed from server/sonar-web/src/main/js/apps/api-documentation/router.js)24
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Menu.js68
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Params.js91
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.js59
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/Search.js86
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js168
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/styles/web-api.css72
-rw-r--r--server/sonar-web/src/main/js/apps/web-api/utils.js (renamed from server/sonar-web/src/main/js/apps/api-documentation/header-view.js)13
26 files changed, 772 insertions, 705 deletions
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/action-view.js b/server/sonar-web/src/main/js/apps/api-documentation/action-view.js
deleted file mode 100644
index 90969fe6e5d..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/action-view.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import $ from 'jquery';
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-action.hbs';
-
-export default Marionette.ItemView.extend({
- className: 'panel panel-vertical',
- template: Template,
-
- modelEvents: {
- 'change': 'render'
- },
-
- events: {
- 'click .js-show-response-example': 'onShowResponseExampleClick',
- 'click .js-hide-response-example': 'onHideResponseExampleClick'
- },
-
- initialize () {
- this.listenTo(this.options.state, 'change', this.toggleHidden);
- },
-
- onRender () {
- this.$el.attr('data-web-service', this.model.get('path'));
- this.$el.attr('data-action', this.model.get('key'));
- this.toggleHidden();
- this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' });
- },
-
- onShowResponseExampleClick (e) {
- e.preventDefault();
- this.fetchResponse();
- },
-
- onHideResponseExampleClick (e) {
- e.preventDefault();
- this.model.unset('responseExample');
- },
-
- fetchResponse () {
- const url = '/api/webservices/response_example';
- const options = { controller: this.model.get('path'), action: this.model.get('key') };
- return $.get(url, options).done(r => {
- this.model.set({ responseExample: r.example });
- });
- },
-
- toggleHidden () {
- const test = this.model.get('path') + '/' + this.model.get('key');
- this.$el.toggleClass('hidden', !this.options.state.match(test, this.model.get('internal')));
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js b/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js
deleted file mode 100644
index c403aed7d6b..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/actions-view.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import $ from 'jquery';
-import Marionette from 'backbone.marionette';
-import ActionView from './action-view';
-
-export default Marionette.CollectionView.extend({
- childView: ActionView,
-
- childViewOptions () {
- return {
- state: this.options.state
- };
- },
-
- scrollToTop () {
- let parent = this.$el.scrollParent();
- if (parent.is(document)) {
- parent = $(window);
- }
- parent.scrollTop(0);
- },
-
- scrollToAction (action) {
- const model = this.collection.findWhere({ key: action });
- if (model != null) {
- const view = this.children.findByModel(model);
- if (view != null) {
- this.scrollToView(view);
- }
- }
- },
-
- scrollToView (view) {
- const elOffset = view.el.getBoundingClientRect();
- if (elOffset != null) {
- const scrollTop = elOffset.top - 70;
- window.scrollTo(0, scrollTop);
- }
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/app.js b/server/sonar-web/src/main/js/apps/api-documentation/app.js
deleted file mode 100644
index 7de5702240f..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/app.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import $ from 'jquery';
-import Backbone from 'backbone';
-import Marionette from 'backbone.marionette';
-import Router from './router';
-import Controller from './controller';
-import Layout from './layout';
-import List from './list';
-import ListView from './list-view';
-import FiltersView from './filters-view';
-import SearchView from './search-view';
-
-const App = new Marionette.Application();
-const init = function () {
- const options = window.sonarqube;
-
- // State
- this.state = new Backbone.Model({ internal: false });
- this.state.match = function (test, internal) {
- const pattern = new RegExp(this.get('query'), 'i');
- const internalCheck = !this.get('internal') && internal;
- return test.search(pattern) !== -1 && !internalCheck;
- };
-
- // Layout
- this.layout = new Layout({ el: options.el });
- this.layout.render();
- $('#footer').addClass('search-navigator-footer');
-
- // Web Services List
- this.list = new List();
-
- // Controller
- this.controller = new Controller({
- app: this,
- state: this.state
- });
-
- // List View
- this.listView = new ListView({
- collection: this.list,
- state: this.state
- });
- this.layout.resultsRegion.show(this.listView);
-
- // Filters View
- this.filtersView = new FiltersView({
- collection: this.list,
- state: this.state
- });
- this.layout.actionsRegion.show(this.filtersView);
-
- // Search View
- this.searchView = new SearchView({
- state: this.state
- });
- this.layout.searchRegion.show(this.searchView);
-
- // Router
- this.router = new Router({ app: this });
- Backbone.history.start({
- pushState: true,
- root: options.urlRoot
- });
-};
-
-App.on('start', function (options) {
- init.call(App, options);
-});
-
-window.sonarqube.appStarted.then(options => App.start(options));
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/controller.js b/server/sonar-web/src/main/js/apps/api-documentation/controller.js
deleted file mode 100644
index db088e1d86a..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/controller.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Backbone from 'backbone';
-import Marionette from 'backbone.marionette';
-import ActionsView from './actions-view';
-import HeaderView from './header-view';
-
-export default Marionette.Controller.extend({
- initialize (options) {
- this.list = options.app.list;
- this.listenTo(this.list, 'select', this.onItemSelect);
- },
-
- show (path) {
- const that = this;
- this.fetchList().done(function () {
- if (path) {
- const item = that.list.findWhere({ path });
- if (item != null) {
- that.showWebService(path);
- } else {
- that.showAction(path);
- }
- }
- });
- },
-
- showWebService (path) {
- const item = this.list.findWhere({ path });
- if (item != null) {
- item.trigger('select', item);
- }
- },
-
- showAction (path) {
- const webService = this.list.find(function (item) {
- return path.indexOf(item.get('path')) === 0;
- });
- if (webService != null) {
- const action = path.substr(webService.get('path').length + 1);
- webService.trigger('select', webService, { trigger: false, action });
- }
- },
-
- onItemSelect (item, options) {
- const path = item.get('path');
- const opts = _.defaults(options || {}, { trigger: true });
- if (opts.trigger) {
- this.options.app.router.navigate(path);
- }
- this.options.app.listView.highlight(path);
-
- if (item.get('internal')) {
- this.options.state.set({ internal: true });
- }
-
- const actions = new Backbone.Collection(item.get('actions'));
- const actionsView = new ActionsView({
- collection: actions,
- state: this.options.state
- });
- this.options.app.layout.detailsRegion.show(actionsView);
- this.options.app.layout.headerRegion.show(new HeaderView({ model: item }));
-
- if (opts.action != null) {
- const model = actions.findWhere({ key: opts.action });
- if (model) {
- if (model.get('internal')) {
- this.options.state.set({ internal: true });
- }
- actionsView.scrollToAction(opts.action);
- }
- } else {
- actionsView.scrollToTop();
- }
- },
-
- fetchList () {
- return this.list.fetch({ data: { 'include_internals': true } });
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js b/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js
deleted file mode 100644
index 5e835c3b462..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/filters-view.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-filters.hbs';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- events: {
- 'change .js-toggle-internal': 'toggleInternal'
- },
-
- initialize () {
- this.listenTo(this.options.state, 'change:internal', this.render);
- },
-
- toggleInternal () {
- this.options.state.set({ internal: !this.options.state.get('internal') });
- },
-
- serializeData () {
- return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
- state: this.options.state.toJSON()
- });
- }
-});
-
-
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/item-view.js b/server/sonar-web/src/main/js/apps/api-documentation/item-view.js
deleted file mode 100644
index 2c6e4f121af..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/item-view.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-web-service.hbs';
-
-export default Marionette.ItemView.extend({
- tagName: 'a',
- className: 'list-group-item',
- template: Template,
-
- modelEvents: {
- 'change': 'render'
- },
-
- events: {
- 'click': 'onClick'
- },
-
- initialize () {
- this.listenTo(this.options.state, 'change:query', this.toggleHidden);
- this.listenTo(this.options.state, 'change:internal', this.toggleHidden);
- },
-
- shouldBeHidden () {
- const that = this;
- const match = this.options.state.match(this.model.get('path')) ||
- _.some(this.model.get('actions'), function (action) {
- const test = action.path + '/' + action.key;
- return that.options.state.match(test, action.internal);
- });
-
- const showInternal = this.options.state.get('internal');
- const hideMe = this.model.get('internal') && !showInternal;
- return !match || hideMe;
- },
-
- onRender () {
- this.$el.attr('data-path', this.model.get('path'));
- this.$el.toggleClass('active', this.options.highlighted);
- this.toggleHidden();
- this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'right' });
- },
-
- onClick (e) {
- e.preventDefault();
- this.model.trigger('select', this.model);
- },
-
- toggleHidden () {
- this.$el.toggleClass('hidden', this.shouldBeHidden());
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/layout.js b/server/sonar-web/src/main/js/apps/api-documentation/layout.js
deleted file mode 100644
index 51d449a989d..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/layout.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-layout.hbs';
-
-export default Marionette.LayoutView.extend({
- template: Template,
-
- regions: {
- headerRegion: '.search-navigator-workspace-header',
- actionsRegion: '.search-navigator-filters',
- searchRegion: '.api-documentation-search',
- resultsRegion: '.api-documentation-results',
- detailsRegion: '.search-navigator-workspace-details'
- },
-
- onRender () {
- const navigator = this.$('.search-navigator');
- navigator.addClass('sticky search-navigator-extended-view');
- const top = navigator.offset().top;
- this.$('.search-navigator-workspace-header').css({ top });
- this.$('.search-navigator-side').css({ top }).isolatedScroll();
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/search-view.js b/server/sonar-web/src/main/js/apps/api-documentation/search-view.js
deleted file mode 100644
index b837e3f5f89..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/search-view.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import _ from 'underscore';
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-search.hbs';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- ui: {
- input: '.search-box-input'
- },
-
- events: {
- 'keyup @ui.input': 'onChange',
- 'search @ui.input': 'onChange'
- },
-
- initialize () {
- this.query = '';
- this.debouncedFilter = _.debounce(this.filter, 250);
- },
-
- onChange () {
- const query = this.ui.input.val();
- if (query === this.query) {
- return;
- }
- this.query = this.ui.input.val();
- this.debouncedFilter(query);
- },
-
- filter (query) {
- this.options.state.set({ query });
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs
deleted file mode 100644
index bf2e964eab5..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-action.hbs
+++ /dev/null
@@ -1,90 +0,0 @@
-<header class="page-header">
- <h3 class="page-title big">{{#if post}}POST{{else}}GET{{/if}} {{this.path}}/{{key}}</h3>
-
- <div class="page-actions">
- {{#if internal}}
- <span class="spacer-right">
- <span class="badge" data-toggle="tooltip" title="{{t 'api_documentation.internal_tooltip'}}">internal</span>
- </span>
- {{/if}}
- {{#if deprecatedSince}}
- <span class="spacer-right">
- <span class="badge badge-danger" data-toggle="tooltip"
- title="{{t 'api_documentation.deprecation_tooltip'}}">Deprecated since {{deprecatedSince}}</span>
- </span>
- {{/if}}
- {{#if since}}
- <span class="spacer-right">
- <span class="note">Since {{since}}</span>
- </span>
- {{/if}}
-
- <a class="js-permalink icon-link" href="{{link '/api_documentation/' this.path '/' key}}" target="_blank"></a>
- </div>
-</header>
-
-<div class="markdown">{{{description}}}</div>
-
-{{#if params}}
- <h4 class="spacer-top little-spacer-bottom">Parameters</h4>
- <table class="width-100 data zebra">
- {{#each params}}
- <tr>
- <td style="width: 10em;">
- <code>{{key}}</code>
- <div class="note">{{#if required}}required{{else}}optional{{/if}}</div>
- {{#if since}}
- <div class="note">since {{since}}</div>
- {{/if}}
- {{#if deprecatedSince}}
- <span class="badge badge-danger little-spacer-top" data-toggle="tooltip"
- title="{{t 'api_documentation.deprecation_tooltip'}}">deprecated since {{deprecatedSince}}</span>
- {{/if}}
- </td>
- <td>
- <div class="markdown">{{{description}}}</div>
-
- {{#if possibleValues}}
- <p class="little-spacer-top">
- <strong>Possible values:</strong>
- </p>
- <ul class="list-styled">
- {{#each possibleValues}}
- <li class="little-spacer-top"><code>{{this}}</code></li>
- {{/each}}
- </ul>
- {{/if}}
-
- {{#if defaultValue}}
- <p class="little-spacer-top">
- <strong>Default value:</strong> <code>{{defaultValue}}</code>
- </p>
- {{/if}}
-
- {{#if exampleValue}}
- <p class="little-spacer-top">
- <strong>Example value:</strong> <code>{{exampleValue}}</code>
- </p>
- {{/if}}
- </td>
- </tr>
- {{/each}}
- </table>
-{{/if}}
-
-{{#if hasResponseExample}}
- <h4 class="spacer-top">
- Example Response
- {{#if responseExample}}
- <a class="js-hide-response-example little-spacer-left" href="#">Hide</a>
- {{else}}
- <a class="js-show-response-example little-spacer-left" href="#">Show</a>
- {{/if}}
- </h4>
-
- {{#if responseExample}}
- <div class="little-spacer-top">
- <pre style="white-space: pre-wrap;">{{responseExample}}</pre>
- </div>
- {{/if}}
-{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs
deleted file mode 100644
index 5941110db0a..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-filters.hbs
+++ /dev/null
@@ -1,6 +0,0 @@
-<h1 class="page-title">Web Service API</h1>
-
-<div class="page-actions">
- <input class="js-toggle-internal" type="checkbox" id="api-documentation-show-internal" {{#if state.internal}}checked{{/if}}>
- <label for="api-documentation-show-internal">Show Internal</label>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs
deleted file mode 100644
index f0d5078b9a0..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-header.hbs
+++ /dev/null
@@ -1 +0,0 @@
-<h2 class="search-navigator-header-component">{{this.path}}</h2>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs
deleted file mode 100644
index f530a848360..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-layout.hbs
+++ /dev/null
@@ -1,12 +0,0 @@
-<div class="search-navigator sticky">
- <div class="search-navigator-side search-navigator-side-light">
- <div class="search-navigator-filters"></div>
- <div class="api-documentation-search"></div>
- <div class="api-documentation-results panel"></div>
- </div>
-
- <div class="search-navigator-workspace">
- <div class="search-navigator-workspace-header"></div>
- <div class="search-navigator-workspace-details"></div>
- </div>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-search.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-search.hbs
deleted file mode 100644
index 6cad4e381b9..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-search.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-<form id="api-documentation-search-form" class="search-box">
- <span id="api-documentation-search-submit" class="search-box-submit button-clean"><i class="icon-search"></i></span>
- <input id="api-documentation-search-query" class="search-box-input" type="search" name="q" placeholder="Search" maxlength="100">
-</form>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs b/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs
deleted file mode 100644
index 5d8fc4b22a7..00000000000
--- a/server/sonar-web/src/main/js/apps/api-documentation/templates/api-documentation-web-service.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-<h3 class="list-group-item-heading">
- {{this.path}}
- {{#if internal}}
- <span class="badge" data-toggle="tooltip" title="{{t 'api_documentation.internal_tooltip'}}">internal</span>
- {{/if}}
-</h3>
-<p class="list-group-item-text">{{{description}}}</p>
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/list.js b/server/sonar-web/src/main/js/apps/web-api/app.js
index 3ecd8fda7e1..0ff00d28a30 100644
--- a/server/sonar-web/src/main/js/apps/api-documentation/list.js
+++ b/server/sonar-web/src/main/js/apps/web-api/app.js
@@ -17,25 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import _ from 'underscore';
-import Backbone from 'backbone';
+import React from 'react';
+import { render } from 'react-dom';
+import { Router, Route, Redirect, useRouterHistory } from 'react-router';
+import { createHistory } from 'history';
-export default Backbone.Collection.extend({
- url: '/api/webservices/list',
- comparator: 'path',
+import WebApiApp from './components/WebApiApp';
+import './styles/web-api.css';
- parse (r) {
- return r.webServices.map(function (webService) {
- const internal = _.every(webService.actions, function (action) {
- return action.internal;
- });
- const actions = webService.actions.map(function (action) {
- return _.extend(action, { path: webService.path });
- });
- return _.extend(webService, {
- internal,
- actions
- });
- });
- }
+window.sonarqube.appStarted.then(options => {
+ const el = document.querySelector(options.el);
+
+ const history = useRouterHistory(createHistory)({
+ basename: window.sonarqube.urlRoot
+ });
+
+ render((
+ <Router history={history}>
+ <Redirect from="/index" to="/"/>
+ <Route path="/**" component={WebApiApp}/>
+ </Router>
+ ), el);
});
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Action.js b/server/sonar-web/src/main/js/apps/web-api/components/Action.js
new file mode 100644
index 00000000000..7e9c41cbc43
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Action.js
@@ -0,0 +1,117 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+import { Link } from 'react-router';
+
+import { getActionKey } from '../utils';
+import Params from './Params';
+import ResponseExample from './ResponseExample';
+import DeprecatedBadge from './DeprecatedBadge';
+import InternalBadge from './InternalBadge';
+
+export default class Action extends React.Component {
+ state = {
+ showParams: false,
+ showResponse: false
+ };
+
+ handleShowParamsClick (e) {
+ e.preventDefault();
+ this.refs.toggleParameters.blur();
+ this.setState({ showParams: !this.state.showParams });
+ }
+
+ handleShowResponseClick (e) {
+ e.preventDefault();
+ this.refs.toggleResponse.blur();
+ this.setState({ showResponse: !this.state.showResponse });
+ }
+
+ render () {
+ const { action, domain } = this.props;
+ const { showParams, showResponse } = this.state;
+ const verb = action.post ? 'POST' : 'GET';
+ const actionKey = getActionKey(domain.path, action.key);
+
+ return (
+ <div id={actionKey} className="web-api-action">
+ <header className="web-api-action-header">
+ <Link
+ to={{ pathname: '/' + actionKey }}
+ className="spacer-right icon-link"/>
+
+ <h3 className="web-api-action-title">
+ {verb}&nbsp;{actionKey}
+ </h3>
+
+ {action.internal && (
+ <span className="spacer-left">
+ <InternalBadge/>
+ </span>
+ )}
+
+ {action.since && (
+ <span className="spacer-left badge">since {action.since}</span>
+ )}
+
+ {action.deprecatedSince && (
+ <span className="spacer-left">
+ <DeprecatedBadge since={action.deprecatedSince}/>
+ </span>
+ )}
+ </header>
+
+ <div
+ className="web-api-action-description markdown"
+ dangerouslySetInnerHTML={{ __html: action.description }}/>
+
+ {(action.params || action.hasResponseExample) && (
+ <ul className="web-api-action-actions list-inline">
+ {action.params && (
+ <li>
+ <a
+ ref="toggleParameters"
+ onClick={this.handleShowParamsClick.bind(this)}
+ href="#">
+ {showParams ? 'Hide Parameters' : 'Show Parameters'}
+ </a>
+ </li>
+ )}
+
+ {action.hasResponseExample && (
+ <li>
+ <a
+ ref="toggleResponse"
+ onClick={this.handleShowResponseClick.bind(this)}
+ href="#">
+ {showResponse ? 'Hide Response Example' : 'Show Response Example'}
+ </a>
+ </li>
+ )}
+ </ul>
+ )}
+
+ {showParams && action.params && <Params params={action.params}/>}
+
+ {showResponse && action.hasResponseExample && <ResponseExample domain={domain} action={action}/>}
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/list-view.js b/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.js
index c6319e58236..212e6622420 100644
--- a/server/sonar-web/src/main/js/apps/api-documentation/list-view.js
+++ b/server/sonar-web/src/main/js/apps/web-api/components/DeprecatedBadge.js
@@ -17,23 +17,18 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Marionette from 'backbone.marionette';
-import ItemView from './item-view';
+import React from 'react';
-export default Marionette.CollectionView.extend({
- className: 'list-group',
- childView: ItemView,
+import { translate } from '../../../helpers/l10n';
- childViewOptions (model) {
- return {
- collectionView: this,
- highlighted: model.get('path') === this.highlighted,
- state: this.options.state
- };
- },
+export default function DeprecatedBadge ({ since }) {
+ const label = since ? `deprecated since ${since}` : 'deprecated';
- highlight (path) {
- this.highlighted = path;
- this.render();
- }
-});
+ return (
+ <span
+ className="badge badge-warning"
+ title={translate('api_documentation.deprecation_tooltip')}>
+ {label}
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Domain.js b/server/sonar-web/src/main/js/apps/web-api/components/Domain.js
new file mode 100644
index 00000000000..df212f6df13
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Domain.js
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+
+import Action from './Action';
+import InternalBadge from './InternalBadge';
+import { getActionKey } from '../utils';
+
+export default function Domain ({ domain, showInternal, showOnlyDeprecated, searchQuery }) {
+ const filteredActions = domain.actions
+ .filter(action => {
+ return showInternal || !action.internal;
+ })
+ .filter(action => {
+ return !showOnlyDeprecated || (showOnlyDeprecated && action.deprecatedSince);
+ })
+ .filter(action => {
+ const actionKey = getActionKey(domain.path, action.key);
+ return actionKey.indexOf(searchQuery) !== -1;
+ });
+
+ return (
+ <div className="web-api-domain">
+ <header className="web-api-domain-header">
+ <h2 className="web-api-domain-title">{domain.path}</h2>
+
+ {domain.internal && (
+ <span className="spacer-left">
+ <InternalBadge/>
+ </span>
+ )}
+ </header>
+
+ {domain.description && (
+ <p className="web-api-domain-description">{domain.description}</p>
+ )}
+
+ <div className="web-api-domain-actions">
+ {filteredActions.map(action => (
+ <Action
+ key={getActionKey(domain.path, action.key)}
+ action={action}
+ domain={domain}
+ location={location}/>
+ ))}
+ </div>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/router.js b/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.js
index 45193995ad9..cfb87e6c841 100644
--- a/server/sonar-web/src/main/js/apps/api-documentation/router.js
+++ b/server/sonar-web/src/main/js/apps/web-api/components/InternalBadge.js
@@ -17,18 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Backbone from 'backbone';
+import React from 'react';
-export default Backbone.Router.extend({
- routes: {
- '*path': 'show'
- },
+import { translate } from '../../../helpers/l10n';
- initialize (options) {
- this.app = options.app;
- },
-
- show (path) {
- this.app.controller.show(path);
- }
-});
+export default function InternalBadge () {
+ return (
+ <span
+ className="badge badge-danger"
+ title={translate('api_documentation.internal_tooltip')}>
+ internal
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Menu.js b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js
new file mode 100644
index 00000000000..e5d58f5de94
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Menu.js
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+import { Link } from 'react-router';
+import classNames from 'classnames';
+
+import InternalBadge from './InternalBadge';
+import { getActionKey } from '../utils';
+
+export default function Menu ({ domains, showInternal, showOnlyDeprecated, searchQuery, splat }) {
+ const filteredDomains = (domains || [])
+ .map(domain => {
+ const filteredActions = domain.actions
+ .filter(action => {
+ return showInternal || !action.internal;
+ })
+ .filter(action => {
+ return !showOnlyDeprecated || (showOnlyDeprecated && action.deprecatedSince);
+ })
+ .filter(action => {
+ const actionKey = getActionKey(domain.path, action.key);
+ return actionKey.indexOf(searchQuery) !== -1;
+ });
+ return { ...domain, filteredActions };
+ })
+ .filter(domain => domain.filteredActions.length);
+
+
+ return (
+ <div className="api-documentation-results panel">
+ <div className="list-group">
+ {filteredDomains.map(domain => (
+ <Link
+ key={domain.path}
+ className={classNames('list-group-item', { 'active': splat.indexOf(domain.path) === 0 })}
+ to={domain.path}>
+ <h3 className="list-group-item-heading">
+ {domain.path}
+ {domain.internal && (
+ <InternalBadge/>
+ )}
+ </h3>
+ <p className="list-group-item-text">
+ {domain.description}
+ </p>
+ </Link>
+ ))}
+ </div>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Params.js b/server/sonar-web/src/main/js/apps/web-api/components/Params.js
new file mode 100644
index 00000000000..66018f8ab26
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Params.js
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+
+export default function Params ({ params }) {
+ return (
+ <div className="web-api-params">
+ <table>
+ <tbody>
+ {params.map(param => (
+ <tr key={param.key}>
+ <td style={{ width: 150 }}>
+ <code>{param.key}</code>
+
+ <div className="note little-spacer-top">
+ {param.required ? 'required' : 'optional'}
+ </div>
+
+ {param.since && (
+ <div className="note little-spacer-top">
+ since {param.since}
+ </div>
+ )}
+
+ {param.deprecatedSince && (
+ <div className="little-spacer-top">
+ <span className="badge badge-danger">
+ deprecated since {param.deprecatedSince}
+ </span>
+ </div>
+ )}
+ </td>
+
+ <td>
+ <div
+ className="markdown"
+ dangerouslySetInnerHTML={{ __html: param.description }}/>
+ </td>
+
+ <td style={{ width: 250 }}>
+ {param.possibleValues && (
+ <div>
+ <h4>Possible values</h4>
+ <ul className="list-styled">
+ {param.possibleValues.map(value => (
+ <li key={value} className="little-spacer-top">
+ <code>{value}</code>
+ </li>
+ ))}
+ </ul>
+ </div>
+ )}
+
+ {param.defaultValue && (
+ <div className="little-spacer-top">
+ <h4>Default value</h4>
+ <code>{param.defaultValue}</code>
+ </div>
+ )}
+
+ {param.exampleValue && (
+ <div className="little-spacer-top">
+ <h4>Example value</h4>
+ <code>{param.exampleValue}</code>
+ </div>
+ )}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.js b/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.js
new file mode 100644
index 00000000000..70e1791c5b2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.js
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+
+import { fetchResponseExample as fetchResponseExampleApi } from '../../../api/web-api';
+
+export default class ResponseExample extends React.Component {
+ state = {};
+
+ componentDidMount () {
+ this.mounted = true;
+ this.fetchResponseExample();
+ }
+
+ componentDidUpdate (nextProps) {
+ if (nextProps.action !== this.props.action) {
+ this.fetchResponseExample();
+ }
+ }
+
+ componentWillUnmount () {
+ this.mounted = false;
+ }
+
+ fetchResponseExample () {
+ const { domain, action } = this.props;
+ fetchResponseExampleApi(domain.path, action.key)
+ .then(responseExample => this.setState({ responseExample }));
+ }
+
+ render () {
+ const { responseExample } = this.state;
+
+ return (
+ <div className="web-api-response">
+ {responseExample && (
+ <pre style={{ whiteSpace: 'pre-wrap' }}>{responseExample.example}</pre>
+ )}
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/Search.js b/server/sonar-web/src/main/js/apps/web-api/components/Search.js
new file mode 100644
index 00000000000..9ff60270d1b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/Search.js
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import _ from 'underscore';
+import React from 'react';
+
+import DeprecatedBadge from './DeprecatedBadge';
+import InternalBadge from './InternalBadge';
+import Checkbox from '../../../components/shared/checkbox';
+
+export default class Search extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = { query: '' };
+ this.actuallySearch = _.debounce(this.actuallySearch.bind(this), 250);
+ }
+
+ handleSearch (e) {
+ const { value } = e.target;
+ this.setState({ query: value });
+ this.actuallySearch();
+ }
+
+ actuallySearch () {
+ const { onSearch } = this.props;
+ onSearch(this.state.query);
+ }
+
+ render () {
+ const { showInternal, showOnlyDeprecated, onToggleInternal, onToggleDeprecated } = this.props;
+
+ return (
+ <div className="web-api-search">
+ <div>
+ <i className="icon-search"/>
+ <input
+ className="spacer-left input-large"
+ type="search"
+ value={this.state.query}
+ placeholder="Search..."
+ onChange={this.handleSearch.bind(this)}/>
+ </div>
+
+ <div className="big-spacer-top">
+ <Checkbox
+ initiallyChecked={showInternal}
+ onCheck={onToggleInternal}/>
+ {' '}
+ <span
+ style={{ cursor: 'pointer' }}
+ onClick={onToggleInternal}>
+ <InternalBadge/>
+ </span>
+ </div>
+
+ <div className="spacer-top">
+ <Checkbox
+ initiallyChecked={showOnlyDeprecated}
+ onCheck={onToggleDeprecated}/>
+ {' '}
+ <span
+ style={{ cursor: 'pointer' }}
+ onClick={onToggleDeprecated}>
+ Only <DeprecatedBadge/>
+ </span>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js
new file mode 100644
index 00000000000..332d18f1399
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.js
@@ -0,0 +1,168 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import React from 'react';
+import { Link } from 'react-router';
+
+import { fetchWebApi } from '../../../api/web-api';
+import Menu from './Menu';
+import Search from './Search';
+import Domain from './Domain';
+import { getActionKey } from '../utils';
+
+export default class WebApiApp extends React.Component {
+ state = {
+ domains: [],
+ searchQuery: '',
+ showInternal: false,
+ showOnlyDeprecated: false
+ };
+
+ componentDidMount () {
+ this.mounted = true;
+ this.scrollToAction = this.scrollToAction.bind(this);
+ this.fetchList();
+ document.getElementById('footer').classList.add('search-navigator-footer');
+ }
+
+ componentDidUpdate () {
+ this.toggleInternalInitially();
+ this.scrollToAction();
+ }
+
+ componentWillUnmount () {
+ this.mounted = false;
+ document.getElementById('footer').classList.delete('search-navigator-footer');
+ }
+
+ fetchList (cb) {
+ fetchWebApi().then(domains => {
+ if (this.mounted) {
+ this.setState({ domains }, cb);
+ }
+ });
+ }
+
+ scrollToAction () {
+ const { splat } = this.props.params;
+ this.scrollToElement(splat);
+ }
+
+ scrollToElement (id) {
+ const element = document.getElementById(id);
+
+ if (element) {
+ const rect = element.getBoundingClientRect();
+ const top = rect.top + window.pageYOffset - 20;
+
+ window.scrollTo(0, top);
+ } else {
+ window.scrollTo(0, 0);
+ }
+ }
+
+ toggleInternalInitially () {
+ const { splat } = this.props.params;
+ const { domains, showInternal } = this.state;
+
+ if (!showInternal) {
+ domains.forEach(domain => {
+ if (domain.path === splat && domain.internal) {
+ this.setState({ showInternal: true });
+ }
+ domain.actions.forEach(action => {
+ const actionKey = getActionKey(domain.path, action.key);
+ if (actionKey === splat && action.internal) {
+ this.setState({ showInternal: true });
+ }
+ });
+ });
+ }
+ }
+
+ handleSearch (searchQuery) {
+ this.setState({ searchQuery });
+ }
+
+ handleToggleInternal () {
+ const { splat } = this.props.params;
+ const { router } = this.context;
+ const { domains } = this.state;
+ const domain = domains.find(domain => splat.indexOf(domain.path) === 0);
+ const showInternal = !this.state.showInternal;
+
+ if (domain && domain.internal && !showInternal) {
+ router.push('/');
+ }
+
+ this.setState({ showInternal });
+ }
+
+ handleToggleDeprecated () {
+ this.setState({ showOnlyDeprecated: !this.state.showOnlyDeprecated });
+ }
+
+ render () {
+ const { splat } = this.props.params;
+ const { domains, showInternal, showOnlyDeprecated, searchQuery } = this.state;
+
+ const domain = domains.find(domain => splat.indexOf(domain.path) === 0);
+
+ return (
+ <div className="search-navigator sticky">
+ <div className="search-navigator-side search-navigator-side-light" style={{ top: 30 }}>
+ <div className="web-api-page-header">
+ <Link to="/">
+ <h1>Web API</h1>
+ </Link>
+ </div>
+
+ <Search
+ showInternal={showInternal}
+ showOnlyDeprecated={showOnlyDeprecated}
+ onSearch={this.handleSearch.bind(this)}
+ onToggleInternal={this.handleToggleInternal.bind(this)}
+ onToggleDeprecated={this.handleToggleDeprecated.bind(this)}/>
+
+ <Menu
+ domains={this.state.domains}
+ showInternal={showInternal}
+ showOnlyDeprecated={showOnlyDeprecated}
+ searchQuery={searchQuery}
+ splat={splat}/>
+ </div>
+
+ <div className="search-navigator-workspace">
+ {domain && (
+ <Domain
+ key={domain.path}
+ domain={domain}
+ showInternal={showInternal}
+ showOnlyDeprecated={showOnlyDeprecated}
+ searchQuery={searchQuery}/>
+ )}
+ </div>
+ </div>
+ );
+ }
+}
+
+WebApiApp.contextTypes = {
+ router: React.PropTypes.object.isRequired
+};
diff --git a/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css b/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css
new file mode 100644
index 00000000000..e5b572953b1
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/web-api/styles/web-api.css
@@ -0,0 +1,72 @@
+.web-api-page-header {
+ margin: 10px 20px;
+}
+
+.web-api-search {
+ margin: 20px 10px 0;
+ padding: 0 10px 20px;
+ border-bottom: 1px solid #e6e6e6;
+}
+
+.web-api-search .icon-search {
+ color: #cdcdcd;
+}
+
+.web-api-domain {
+ padding: 10px 20px;
+}
+
+.web-api-domain-header,
+.web-api-action-header {
+ display: flex;
+ align-items: center;
+}
+
+.web-api-domain-title {
+ font-size: 18px;
+ font-weight: 400;
+}
+
+.web-api-domain-description {
+ margin-top: 10px;
+ line-height: 1.5;
+}
+
+.web-api-domain-actions {
+}
+
+.web-api-action {
+ padding-top: 30px;
+}
+
+.web-api-action-title {
+ font-weight: 500;
+}
+
+.web-api-action-description {
+ margin-top: 10px;
+}
+
+.web-api-action-actions {
+ margin-top: 10px;
+}
+
+.web-api-params,
+.web-api-response {
+ margin-top: 10px;
+}
+
+.web-api-params > table {
+ width: 100%;
+ table-layout: fixed;
+}
+
+.web-api-params td {
+ vertical-align: top;
+ padding: 8px 10px;
+ border-top: 1px solid #e6e6e6;
+}
+
+.web-api-params tr:first-child td {
+ border-top: none;
+}
diff --git a/server/sonar-web/src/main/js/apps/api-documentation/header-view.js b/server/sonar-web/src/main/js/apps/web-api/utils.js
index c90eb05db73..6e6de08af2d 100644
--- a/server/sonar-web/src/main/js/apps/api-documentation/header-view.js
+++ b/server/sonar-web/src/main/js/apps/web-api/utils.js
@@ -17,13 +17,6 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import Marionette from 'backbone.marionette';
-import Template from './templates/api-documentation-header.hbs';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- modelEvents: {
- 'change': 'render'
- }
-});
+export function getActionKey (domain, action) {
+ return domain + '/' + action;
+}