Add support for Documentation search
Bug: Issue 6200 Change-Id: Ibcad93bd77835106977e72dea7d62159d5518b67 (cherry picked from commit 3750a92ff1fff76ec2aa0aa92211b4e37c50c0fe)
This commit is contained in:
parent
3f53c23e10
commit
9d197e2d41
@ -89,7 +89,7 @@ SEARCH_BOX = """
|
||||
</button>
|
||||
<script type="text/javascript">
|
||||
var f = function() {
|
||||
window.location = '../#/Documentation/' +
|
||||
window.location = '../#/Documentation/q/' +
|
||||
encodeURIComponent(document.getElementById("docSearch").value);
|
||||
}
|
||||
document.getElementById("searchBox").onclick = f;
|
||||
|
@ -236,7 +236,7 @@ public class Dispatcher {
|
||||
if (matchPrefix(QUERY, token)) {
|
||||
query(token);
|
||||
|
||||
} else if (matchPrefix("/Documentation/", token)) {
|
||||
} else if (matchPrefix("/Documentation/q/", token)) {
|
||||
docSearch(token);
|
||||
|
||||
} else if (matchPrefix("/c/", token)) {
|
||||
|
@ -71,7 +71,15 @@ public class StaticModule extends ServletModule {
|
||||
*/
|
||||
public static final ImmutableList<String> POLYGERRIT_INDEX_PATHS =
|
||||
ImmutableList.of(
|
||||
"/", "/c/*", "/p/*", "/q/*", "/x/*", "/admin/*", "/dashboard/*", "/settings/*");
|
||||
"/",
|
||||
"/c/*",
|
||||
"/p/*",
|
||||
"/q/*",
|
||||
"/x/*",
|
||||
"/admin/*",
|
||||
"/dashboard/*",
|
||||
"/settings/*",
|
||||
"/Documentation/q/*");
|
||||
// TODO(dborowitz): These fragments conflict with the REST API
|
||||
// namespace, so they will need to use a different path.
|
||||
// "/groups/*",
|
||||
|
@ -164,6 +164,7 @@ limitations under the License.
|
||||
CHANGE: 'change',
|
||||
DASHBOARD: 'dashboard',
|
||||
DIFF: 'diff',
|
||||
DOCUMENTATION_SEARCH: 'documentation-search',
|
||||
EDIT: 'edit',
|
||||
GROUP: 'group',
|
||||
PLUGIN_SCREEN: 'plugin-screen',
|
||||
|
@ -148,6 +148,10 @@
|
||||
IMPROPERLY_ENCODED_PLUS: /^\/c\/(.+)\/\ \/(.+)$/,
|
||||
|
||||
PLUGIN_SCREEN: /^\/x\/([\w-]+)\/([\w-]+)\/?/,
|
||||
|
||||
DOCUMENTATION_SEARCH_FILTER: '/Documentation/q/filter::filter',
|
||||
DOCUMENTATION_SEARCH: /^\/Documentation\/q\/(.*)$/,
|
||||
DOCUMENTATION: /^\/Documentation(\/)?(.+)?/,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -845,6 +849,17 @@
|
||||
|
||||
this._mapRoute(RoutePattern.PLUGIN_SCREEN, '_handlePluginScreen');
|
||||
|
||||
this._mapRoute(RoutePattern.DOCUMENTATION_SEARCH_FILTER,
|
||||
'_handleDocumentationSearchRoute');
|
||||
|
||||
// redirects /Documentation/q/* to /Documentation/q/filter:*
|
||||
this._mapRoute(RoutePattern.DOCUMENTATION_SEARCH,
|
||||
'_handleDocumentationSearchRedirectRoute');
|
||||
|
||||
// Makes sure /Documentation/* links work (doin't return 404)
|
||||
this._mapRoute(RoutePattern.DOCUMENTATION,
|
||||
'_handleDocumentationRedirectRoute');
|
||||
|
||||
// Note: this route should appear last so it only catches URLs unmatched
|
||||
// by other patterns.
|
||||
this._mapRoute(RoutePattern.DEFAULT, '_handleDefaultRoute');
|
||||
@ -1422,6 +1437,27 @@
|
||||
this._setParams({view, plugin, screen});
|
||||
},
|
||||
|
||||
_handleDocumentationSearchRoute(data) {
|
||||
this._setParams({
|
||||
view: Gerrit.Nav.View.DOCUMENTATION_SEARCH,
|
||||
filter: data.params.filter || null,
|
||||
});
|
||||
},
|
||||
|
||||
_handleDocumentationSearchRedirectRoute(data) {
|
||||
this._redirect('/Documentation/q/filter:' +
|
||||
encodeURIComponent(data.params[0]));
|
||||
},
|
||||
|
||||
_handleDocumentationRedirectRoute(data) {
|
||||
if (data.params[1]) {
|
||||
location.reload();
|
||||
} else {
|
||||
// Redirect /Documentation to /Documentation/index.html
|
||||
this._redirect('/Documentation/index.html');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Catchall route for when no other route is matched.
|
||||
*/
|
||||
|
@ -157,6 +157,9 @@ limitations under the License.
|
||||
'_handleDefaultRoute',
|
||||
'_handleChangeLegacyRoute',
|
||||
'_handleDiffLegacyRoute',
|
||||
'_handleDocumentationRedirectRoute',
|
||||
'_handleDocumentationSearchRoute',
|
||||
'_handleDocumentationSearchRedirectRoute',
|
||||
'_handleLegacyLinenum',
|
||||
'_handleImproperlyEncodedPlusRoute',
|
||||
'_handlePassThroughRoute',
|
||||
|
@ -0,0 +1,62 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<link rel="import" href="../../../bower_components/polymer/polymer.html">
|
||||
|
||||
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
|
||||
<link rel="import" href="../../../behaviors/gr-list-view-behavior/gr-list-view-behavior.html">
|
||||
<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
|
||||
<link rel="import" href="../../../styles/gr-table-styles.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../shared/gr-list-view/gr-list-view.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<dom-module id="gr-documentation-search">
|
||||
<template>
|
||||
<style include="shared-styles"></style>
|
||||
<style include="gr-table-styles"></style>
|
||||
<gr-list-view
|
||||
filter="[[_filter]]"
|
||||
items=false
|
||||
offset=0
|
||||
loading="[[_loading]]"
|
||||
path="[[_path]]">
|
||||
<table id="list" class="genericList">
|
||||
<tr class="headerRow">
|
||||
<th class="name topHeader">Name</th>
|
||||
<th class="name topHeader"></th>
|
||||
<th class="name topHeader"></th>
|
||||
</tr>
|
||||
<tr id="loading" class$="loadingMsg [[computeLoadingClass(_loading)]]">
|
||||
<td>Loading...</td>
|
||||
</tr>
|
||||
<tbody class$="[[computeLoadingClass(_loading)]]">
|
||||
<template is="dom-repeat" items="[[_documentationSearches]]">
|
||||
<tr class="table">
|
||||
<td class="name">
|
||||
<a href$="[[_computeSearchUrl(item.url)]]">[[item.title]]</a>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</gr-list-view>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-documentation-search.js"></script>
|
||||
</dom-module>
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
Polymer({
|
||||
is: 'gr-documentation-search',
|
||||
|
||||
properties: {
|
||||
/**
|
||||
* URL params passed from the router.
|
||||
*/
|
||||
params: {
|
||||
type: Object,
|
||||
observer: '_paramsChanged',
|
||||
},
|
||||
|
||||
_path: {
|
||||
type: String,
|
||||
readOnly: true,
|
||||
value: '/Documentation',
|
||||
},
|
||||
_documentationSearches: Array,
|
||||
|
||||
_loading: {
|
||||
type: Boolean,
|
||||
value: true,
|
||||
},
|
||||
_filter: {
|
||||
type: String,
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.ListViewBehavior,
|
||||
],
|
||||
|
||||
attached() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('title-change', {title: 'Documentation Search'}));
|
||||
},
|
||||
|
||||
_paramsChanged(params) {
|
||||
this._loading = true;
|
||||
this._filter = this.getFilterValue(params);
|
||||
|
||||
return this._getDocumentationSearches(this._filter);
|
||||
},
|
||||
|
||||
_getDocumentationSearches(filter) {
|
||||
this._documentationSearches = [];
|
||||
return this.$.restAPI.getDocumentationSearches(filter)
|
||||
.then(searches => {
|
||||
// Late response.
|
||||
if (filter !== this._filter || !searches) { return; }
|
||||
this._documentationSearches = searches;
|
||||
this._loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
_computeSearchUrl(url) {
|
||||
if (!url) { return ''; }
|
||||
return this.getBaseUrl() + '/' + url;
|
||||
},
|
||||
});
|
||||
})();
|
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
@license
|
||||
Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||
<title>gr-documentation-search</title>
|
||||
|
||||
<script src="../../../bower_components/page/page.js"></script>
|
||||
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||
<link rel="import" href="gr-documentation-search.html">
|
||||
|
||||
<script>void(0);</script>
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
<gr-documentation-search></gr-documentation-search>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
let counter;
|
||||
const documentationGenerator = () => {
|
||||
return {
|
||||
title: `Gerrit Code Review - REST API Developers Notes${++counter}`,
|
||||
url: 'Documentation/dev-rest-api.html',
|
||||
};
|
||||
};
|
||||
|
||||
suite('gr-documentation-search tests', () => {
|
||||
let element;
|
||||
let documentationSearches;
|
||||
let sandbox;
|
||||
let value;
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.stub(page, 'show');
|
||||
element = fixture('basic');
|
||||
counter = 0;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
suite('list with searches for documentation', () => {
|
||||
setup(done => {
|
||||
documentationSearches = _.times(26, documentationGenerator);
|
||||
stub('gr-rest-api-interface', {
|
||||
getDocumentationSearches() {
|
||||
return Promise.resolve(documentationSearches);
|
||||
},
|
||||
});
|
||||
element._paramsChanged(value).then(() => { flush(done); });
|
||||
});
|
||||
|
||||
test('test for test repo in the list', done => {
|
||||
flush(() => {
|
||||
assert.equal(element._documentationSearches[0].title,
|
||||
'Gerrit Code Review - REST API Developers Notes1');
|
||||
assert.equal(element._documentationSearches[0].url,
|
||||
'Documentation/dev-rest-api.html');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('filter', () => {
|
||||
setup(() => {
|
||||
documentationSearches = _.times(25, documentationGenerator);
|
||||
documentationSearchesFiltered = _.times(1, documentationSearches);
|
||||
});
|
||||
|
||||
test('_paramsChanged', done => {
|
||||
sandbox.stub(element.$.restAPI, 'getDocumentationSearches', () => {
|
||||
return Promise.resolve(documentationSearches);
|
||||
});
|
||||
const value = {
|
||||
filter: 'test',
|
||||
};
|
||||
element._paramsChanged(value).then(() => {
|
||||
assert.isTrue(element.$.restAPI.getDocumentationSearches.lastCall
|
||||
.calledWithExactly('test'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('loading', () => {
|
||||
test('correct contents are displayed', () => {
|
||||
assert.isTrue(element._loading);
|
||||
assert.equal(element.computeLoadingClass(element._loading), 'loading');
|
||||
assert.equal(getComputedStyle(element.$.loading).display, 'block');
|
||||
|
||||
element._loading = false;
|
||||
element._repos = _.times(25, documentationGenerator);
|
||||
|
||||
flushAsynchronousOperations();
|
||||
assert.equal(element.computeLoadingClass(element._loading), '');
|
||||
assert.equal(getComputedStyle(element.$.loading).display, 'none');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
@ -48,6 +48,7 @@ limitations under the License.
|
||||
<link rel="import" href="../styles/shared-styles.html">
|
||||
<link rel="import" href="../styles/themes/app-theme.html">
|
||||
<link rel="import" href="./admin/gr-admin-view/gr-admin-view.html">
|
||||
<link rel="import" href="./documentation/gr-documentation-search/gr-documentation-search.html">
|
||||
<link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
|
||||
<link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
|
||||
<link rel="import" href="./change/gr-change-view/gr-change-view.html">
|
||||
@ -198,6 +199,11 @@ limitations under the License.
|
||||
<template is="dom-if" if="[[_showCLAView]]" restamp="true">
|
||||
<gr-cla-view></gr-cla-view>
|
||||
</template>
|
||||
<template is="dom-if" if="[[_showDocumentationSearch]]" restamp="true">
|
||||
<gr-documentation-search
|
||||
params="[[params]]">
|
||||
</gr-documentation-search>
|
||||
</template>
|
||||
<div id="errorView" class="errorView">
|
||||
<div class="errorEmoji">[[_lastError.emoji]]</div>
|
||||
<div class="errorText">[[_lastError.text]]</div>
|
||||
|
@ -72,6 +72,7 @@
|
||||
_showCLAView: Boolean,
|
||||
_showEditorView: Boolean,
|
||||
_showPluginScreen: Boolean,
|
||||
_showDocumentationSearch: Boolean,
|
||||
/** @type {?} */
|
||||
_viewState: Object,
|
||||
/** @type {?} */
|
||||
@ -315,6 +316,8 @@
|
||||
if (isPluginScreen) {
|
||||
this.async(() => this.set('_showPluginScreen', true), 1);
|
||||
}
|
||||
this.set('_showDocumentationSearch',
|
||||
view === Gerrit.Nav.View.DOCUMENTATION_SEARCH);
|
||||
if (this.params.justRegistered) {
|
||||
this.$.registrationOverlay.open();
|
||||
this.$.registrationDialog.loadData().then(() => {
|
||||
|
@ -2981,6 +2981,22 @@
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} filter
|
||||
* @return {!Promise<?Object>}
|
||||
*/
|
||||
getDocumentationSearches(filter) {
|
||||
filter = filter.trim();
|
||||
const encodedFilter = encodeURIComponent(filter);
|
||||
|
||||
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
|
||||
// supports it.
|
||||
return this._fetchSharedCacheURL({
|
||||
url: `/Documentation/?q=${encodedFilter}`,
|
||||
anonymizedUrl: '/Documentation/?*',
|
||||
});
|
||||
},
|
||||
|
||||
getMergeable(changeNum) {
|
||||
return this._getChangeURLAndFetch({
|
||||
changeNum,
|
||||
|
@ -120,6 +120,7 @@ limitations under the License.
|
||||
'diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html',
|
||||
'diff/gr-selection-action-box/gr-selection-action-box_test.html',
|
||||
'diff/gr-syntax-layer/gr-syntax-layer_test.html',
|
||||
'documentation/gr-documentation-search/gr-documentation-search_test.html',
|
||||
'edit/gr-default-editor/gr-default-editor_test.html',
|
||||
'edit/gr-edit-controls/gr-edit-controls_test.html',
|
||||
'edit/gr-edit-file-controls/gr-edit-file-controls_test.html',
|
||||
|
Loading…
x
Reference in New Issue
Block a user