Merge changes I6f8b9302,If538bbc8,I4052ade1,Id07b2b62,I2014a220,I577ae60e,I87e58dda,I9a72cd28,Ic72892ae
* changes: REST API /projects/$SUGGEST REST API /projects/ REST API /accounts/self/capabilities Define a native REST API client /projects/: Support JSON output format Make ls-projects available on HTTP as GET /projects/ Support alias "self" in queries Update GWT to 2.4.0 Update Gson to 2.1
This commit is contained in:
@@ -23,7 +23,7 @@ group, all projects are listed.
|
|||||||
|
|
||||||
ACCESS
|
ACCESS
|
||||||
------
|
------
|
||||||
Any user who has configured an SSH key.
|
Any user who has configured an SSH key, or by an user over HTTP.
|
||||||
|
|
||||||
SCRIPTING
|
SCRIPTING
|
||||||
---------
|
---------
|
||||||
@@ -64,6 +64,15 @@ Line-feeds are escaped to allow ls-project to keep the
|
|||||||
`all`:: Any type of project.
|
`all`:: Any type of project.
|
||||||
--
|
--
|
||||||
|
|
||||||
|
--format::
|
||||||
|
What output format to display the results in.
|
||||||
|
+
|
||||||
|
--
|
||||||
|
`text`:: Simple text based format.
|
||||||
|
`json`:: Map of JSON objects describing each project.
|
||||||
|
`json_compact`:: Minimized JSON output.
|
||||||
|
--
|
||||||
|
|
||||||
--all::
|
--all::
|
||||||
Display all projects that are accessible by the calling user
|
Display all projects that are accessible by the calling user
|
||||||
account. Besides the projects that the calling user account has
|
account. Besides the projects that the calling user account has
|
||||||
@@ -72,12 +81,43 @@ Line-feeds are escaped to allow ls-project to keep the
|
|||||||
the 'READ' access right is not assigned to the calling user
|
the 'READ' access right is not assigned to the calling user
|
||||||
account).
|
account).
|
||||||
|
|
||||||
|
--limit::
|
||||||
|
Cap the number of results to the first N matches.
|
||||||
|
|
||||||
|
HTTP
|
||||||
|
----
|
||||||
|
This command is also available over HTTP, as `/projects/` for
|
||||||
|
anonymous access and `/a/projects/` for authenticated access.
|
||||||
|
Named options are available as query parameters. Results can
|
||||||
|
be limited to projects matching a prefix by supplying the prefix
|
||||||
|
as part of the URL, for example `/projects/external/` lists only
|
||||||
|
projects whose name start with the string `external/`.
|
||||||
|
|
||||||
|
Over HTTP the `json_compact` output format is assumed if the client
|
||||||
|
explicitly asks for JSON using HTTP header `Accept: application/json`.
|
||||||
|
When any JSON output format is used on HTTP, readers must skip the
|
||||||
|
first line produced. The first line is a garbage JSON string crafted
|
||||||
|
to prevent a browser from executing the response in a script tag.
|
||||||
|
|
||||||
|
Output will be gzip compressed if `Accept-Encoding: gzip` was used
|
||||||
|
by the client in the request headers.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
--------
|
--------
|
||||||
|
|
||||||
List visible projects:
|
List visible projects:
|
||||||
=====
|
=====
|
||||||
$ ssh -p 29418 review.example.com gerrit ls-projects
|
$ ssh -p 29418 review.example.com gerrit ls-projects
|
||||||
|
platform/manifest
|
||||||
|
tools/gerrit
|
||||||
|
tools/gwtorm
|
||||||
|
|
||||||
|
$ curl http://review.example.com/projects/
|
||||||
|
platform/manifest
|
||||||
|
tools/gerrit
|
||||||
|
tools/gwtorm
|
||||||
|
|
||||||
|
$ curl http://review.example.com/projects/tools/
|
||||||
tools/gerrit
|
tools/gerrit
|
||||||
tools/gwtorm
|
tools/gwtorm
|
||||||
=====
|
=====
|
||||||
|
@@ -18,6 +18,7 @@ User Guide
|
|||||||
* link:user-signedoffby.html[Signed-off-by Lines]
|
* link:user-signedoffby.html[Signed-off-by Lines]
|
||||||
* link:access-control.html[Access Controls]
|
* link:access-control.html[Access Controls]
|
||||||
* link:error-messages.html[Error Messages]
|
* link:error-messages.html[Error Messages]
|
||||||
|
* link:rest-api.html[REST API]
|
||||||
* link:user-submodules.html[Subscribing to Git Submodules]
|
* link:user-submodules.html[Subscribing to Git Submodules]
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
|
134
Documentation/rest-api.txt
Normal file
134
Documentation/rest-api.txt
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
Gerrit Code Review - REST API
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Gerrit Code Review comes with a REST like API available over HTTP.
|
||||||
|
The API is suitable for automated tools to build upon, as well as
|
||||||
|
supporting some ad-hoc scripting use cases.
|
||||||
|
|
||||||
|
Protocol Details
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Authentication
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
By default all REST endpoints assume anonymous access and filter
|
||||||
|
results to correspond to what anonymous users can read (which may
|
||||||
|
be nothing at all).
|
||||||
|
|
||||||
|
Users (and programs) may authenticate using HTTP authentication by
|
||||||
|
supplying the HTTP password from the user's account settings page.
|
||||||
|
Gerrit by default uses HTTP digest authentication. To authenticate,
|
||||||
|
prefix the endpoint URL with `/a/`. For example to authenticate to
|
||||||
|
`/projects/` request URL `/a/projects/`.
|
||||||
|
|
||||||
|
Output Format
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
Most APIs return text format by default. JSON can be requested
|
||||||
|
by setting the `Accept` HTTP request header to include
|
||||||
|
`application/json`, for example:
|
||||||
|
|
||||||
|
----
|
||||||
|
GET /projects/ HTTP/1.0
|
||||||
|
Accept: application/json
|
||||||
|
----
|
||||||
|
|
||||||
|
JSON responses are encoded using UTF-8 and use content type
|
||||||
|
`application/json`. The JSON response body starts with magic prefix
|
||||||
|
line that must be stripped before feeding the rest of the response
|
||||||
|
body to a JSON parser:
|
||||||
|
|
||||||
|
----
|
||||||
|
)]}'
|
||||||
|
[ ... valid JSON ... ]
|
||||||
|
----
|
||||||
|
|
||||||
|
The default JSON format is `JSON_COMPACT`, which skips unnecessary
|
||||||
|
whitespace. This is not the easiest format for a human to read. Many
|
||||||
|
examples in this documentation use `format=JSON` as a query parameter
|
||||||
|
to obtain pretty formatting in the response. Producing (and parsing)
|
||||||
|
the compact format is more efficient, so most tools should prefer the
|
||||||
|
default compact format.
|
||||||
|
|
||||||
|
Responses will be gzip compressed by the server if the HTTP
|
||||||
|
`Accept-Encoding` request header is set to `gzip`. This may
|
||||||
|
save on network transfer time for larger responses.
|
||||||
|
|
||||||
|
Endpoints
|
||||||
|
---------
|
||||||
|
|
||||||
|
[[accounts_self_capabilities]]
|
||||||
|
/accounts/self/capabilities (Account Capabilities)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Returns the global capabilities (such as createProject or
|
||||||
|
createGroup) that are enabled for the calling user. This can be used
|
||||||
|
by UI tools to discover if administrative features are available
|
||||||
|
to the caller, so they can hide (or show) relevant UI actions.
|
||||||
|
|
||||||
|
----
|
||||||
|
GET /accounts/self/capabilities?format=JSON HTTP/1.0
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"queryLimit": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Administrator that has authenticated with digest authentication:
|
||||||
|
----
|
||||||
|
GET /a/accounts/self/capabilities?format=JSON HTTP/1.0
|
||||||
|
Authorization: Digest username="admin", realm="Gerrit Code Review", nonce="...
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"administrateServer": true,
|
||||||
|
"queryLimit": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 500
|
||||||
|
},
|
||||||
|
"createAccount": true,
|
||||||
|
"createGroup": true,
|
||||||
|
"createProject": true,
|
||||||
|
"killTask": true,
|
||||||
|
"viewCaches": true,
|
||||||
|
"flushCaches": true,
|
||||||
|
"viewConnections": true,
|
||||||
|
"viewQueue": true,
|
||||||
|
"startReplication": true
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
/projects/ (List Projects)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Lists the projects accessible by the caller. This is the same as
|
||||||
|
using the link:cmd-ls-projects.html[ls-projects] command over SSH,
|
||||||
|
and accepts the same options as query parameters.
|
||||||
|
|
||||||
|
----
|
||||||
|
GET /projects/?format=JSON&d HTTP/1.0
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Disposition: attachment
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
|
||||||
|
)]}'
|
||||||
|
{
|
||||||
|
"external/bison": {
|
||||||
|
"description": "GNU parser generator"
|
||||||
|
},
|
||||||
|
"external/gcc": {},
|
||||||
|
"external/openssl": {
|
||||||
|
"description": "encryption\ncrypto routines"
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"description": "\u003chtml\u003e is escaped"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
GERRIT
|
||||||
|
------
|
||||||
|
Part of link:index.html[Gerrit Code Review]
|
@@ -75,7 +75,8 @@ Change-Id that was scraped out of the commit message.
|
|||||||
[[owner]]
|
[[owner]]
|
||||||
owner:'USER'::
|
owner:'USER'::
|
||||||
+
|
+
|
||||||
Changes originally submitted by 'USER'.
|
Changes originally submitted by 'USER'. The special case of
|
||||||
|
`owner:self` will find changes owned by the caller.
|
||||||
|
|
||||||
[[ownerin]]
|
[[ownerin]]
|
||||||
ownerin:'GROUP'::
|
ownerin:'GROUP'::
|
||||||
@@ -85,7 +86,9 @@ Changes originally submitted by a user in 'GROUP'.
|
|||||||
[[reviewer]]
|
[[reviewer]]
|
||||||
reviewer:'USER'::
|
reviewer:'USER'::
|
||||||
+
|
+
|
||||||
Changes that have been, or need to be, reviewed by 'USER'.
|
Changes that have been, or need to be, reviewed by 'USER'. The
|
||||||
|
special case of `reviewer:self` will find changes where the caller
|
||||||
|
has been added as a reviewer.
|
||||||
|
|
||||||
[[reviewerin]]
|
[[reviewerin]]
|
||||||
reviewerin:'GROUP'::
|
reviewerin:'GROUP'::
|
||||||
@@ -213,6 +216,16 @@ is:reviewed::
|
|||||||
True if there is at least one non-zero score on the change, in any
|
True if there is at least one non-zero score on the change, in any
|
||||||
approval category, by any user.
|
approval category, by any user.
|
||||||
|
|
||||||
|
is:owner::
|
||||||
|
+
|
||||||
|
True on any change where the current user is the change owner.
|
||||||
|
Same as `owner:self`.
|
||||||
|
|
||||||
|
is:reviewer::
|
||||||
|
+
|
||||||
|
True on any change where the current user is a reviewer.
|
||||||
|
Same as `reviewer:self`.
|
||||||
|
|
||||||
is:open::
|
is:open::
|
||||||
+
|
+
|
||||||
True if the change is other open or submitted, merge pending.
|
True if the change is other open or submitted, merge pending.
|
||||||
@@ -373,16 +386,20 @@ the change. This flag is always added to any query.
|
|||||||
starredby:'USER'::
|
starredby:'USER'::
|
||||||
+
|
+
|
||||||
Matches changes that have been starred by 'USER'.
|
Matches changes that have been starred by 'USER'.
|
||||||
|
The special case `starredby:self` applies to the caller.
|
||||||
|
|
||||||
watchedby:'USER'::
|
watchedby:'USER'::
|
||||||
+
|
+
|
||||||
Matches changes that 'USER' has configured watch filters for.
|
Matches changes that 'USER' has configured watch filters for.
|
||||||
|
The special case `watchedby:self` applies to the caller.
|
||||||
|
|
||||||
draftby:'USER'::
|
draftby:'USER'::
|
||||||
+
|
+
|
||||||
Matches changes that 'USER' has left unpublished drafts on.
|
Matches changes that 'USER' has left unpublished drafts on.
|
||||||
Since the drafts are unpublished, it is not possible to see the
|
Since the drafts are unpublished, it is not possible to see the
|
||||||
draft text, or even how many drafts there are.
|
draft text, or even how many drafts there are. The special case
|
||||||
|
of `draftby:self` will find changes where the caller has created
|
||||||
|
a draft comment.
|
||||||
|
|
||||||
limit:'CNT'::
|
limit:'CNT'::
|
||||||
+
|
+
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
package com.google.gerrit.common.data;
|
package com.google.gerrit.common.data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Server wide capabilities. Represented as {@link Permission} objects. */
|
/** Server wide capabilities. Represented as {@link Permission} objects. */
|
||||||
@@ -73,23 +75,34 @@ public class GlobalCapability {
|
|||||||
/** Can view all pending tasks in the queue (not just the filtered set). */
|
/** Can view all pending tasks in the queue (not just the filtered set). */
|
||||||
public static final String VIEW_QUEUE = "viewQueue";
|
public static final String VIEW_QUEUE = "viewQueue";
|
||||||
|
|
||||||
|
private static final List<String> NAMES_ALL;
|
||||||
private static final List<String> NAMES_LC;
|
private static final List<String> NAMES_LC;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
NAMES_LC = new ArrayList<String>();
|
NAMES_ALL = new ArrayList<String>();
|
||||||
NAMES_LC.add(ADMINISTRATE_SERVER.toLowerCase());
|
NAMES_ALL.add(ADMINISTRATE_SERVER);
|
||||||
NAMES_LC.add(CREATE_ACCOUNT.toLowerCase());
|
NAMES_ALL.add(CREATE_ACCOUNT);
|
||||||
NAMES_LC.add(CREATE_GROUP.toLowerCase());
|
NAMES_ALL.add(CREATE_GROUP);
|
||||||
NAMES_LC.add(CREATE_PROJECT.toLowerCase());
|
NAMES_ALL.add(CREATE_PROJECT);
|
||||||
NAMES_LC.add(EMAIL_REVIEWERS.toLowerCase());
|
NAMES_ALL.add(EMAIL_REVIEWERS);
|
||||||
NAMES_LC.add(FLUSH_CACHES.toLowerCase());
|
NAMES_ALL.add(FLUSH_CACHES);
|
||||||
NAMES_LC.add(KILL_TASK.toLowerCase());
|
NAMES_ALL.add(KILL_TASK);
|
||||||
NAMES_LC.add(PRIORITY.toLowerCase());
|
NAMES_ALL.add(PRIORITY);
|
||||||
NAMES_LC.add(QUERY_LIMIT.toLowerCase());
|
NAMES_ALL.add(QUERY_LIMIT);
|
||||||
NAMES_LC.add(START_REPLICATION.toLowerCase());
|
NAMES_ALL.add(START_REPLICATION);
|
||||||
NAMES_LC.add(VIEW_CACHES.toLowerCase());
|
NAMES_ALL.add(VIEW_CACHES);
|
||||||
NAMES_LC.add(VIEW_CONNECTIONS.toLowerCase());
|
NAMES_ALL.add(VIEW_CONNECTIONS);
|
||||||
NAMES_LC.add(VIEW_QUEUE.toLowerCase());
|
NAMES_ALL.add(VIEW_QUEUE);
|
||||||
|
|
||||||
|
NAMES_LC = new ArrayList<String>(NAMES_ALL.size());
|
||||||
|
for (String name : NAMES_ALL) {
|
||||||
|
NAMES_LC.add(name.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return all valid capability names. */
|
||||||
|
public static Collection<String> getAllNames() {
|
||||||
|
return Collections.unmodifiableList(NAMES_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the name is recognized as a capability name. */
|
/** @return true if the name is recognized as a capability name. */
|
||||||
|
@@ -26,9 +26,6 @@ import java.util.List;
|
|||||||
|
|
||||||
@RpcImpl(version = Version.V2_0)
|
@RpcImpl(version = Version.V2_0)
|
||||||
public interface SuggestService extends RemoteJsonService {
|
public interface SuggestService extends RemoteJsonService {
|
||||||
void suggestProjectNameKey(String query, int limit,
|
|
||||||
AsyncCallback<List<Project.NameKey>> callback);
|
|
||||||
|
|
||||||
void suggestAccount(String query, Boolean enabled, int limit,
|
void suggestAccount(String query, Boolean enabled, int limit,
|
||||||
AsyncCallback<List<AccountInfo>> callback);
|
AsyncCallback<List<AccountInfo>> callback);
|
||||||
|
|
||||||
|
@@ -56,6 +56,10 @@ public class RpcStatus implements RpcStartHandler, RpcCompleteHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRpcStart(final RpcStartEvent event) {
|
public void onRpcStart(final RpcStartEvent event) {
|
||||||
|
onRpcStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRpcStart() {
|
||||||
if (++activeCalls == 1) {
|
if (++activeCalls == 1) {
|
||||||
if (hideDepth == 0) {
|
if (hideDepth == 0) {
|
||||||
loading.setVisible(true);
|
loading.setVisible(true);
|
||||||
@@ -65,6 +69,10 @@ public class RpcStatus implements RpcStartHandler, RpcCompleteHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRpcComplete(final RpcCompleteEvent event) {
|
public void onRpcComplete(final RpcCompleteEvent event) {
|
||||||
|
onRpcComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRpcComplete() {
|
||||||
if (--activeCalls == 0) {
|
if (--activeCalls == 0) {
|
||||||
loading.setVisible(false);
|
loading.setVisible(false);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.account;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.rpc.RestApi;
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
|
|
||||||
|
/** Capabilities the caller has from {@code /accounts/self/capabilities}. */
|
||||||
|
public class AccountCapabilities extends JavaScriptObject {
|
||||||
|
public static void all(AsyncCallback<AccountCapabilities> cb, String... filter) {
|
||||||
|
RestApi api = new RestApi("/accounts/self/capabilities");
|
||||||
|
for (String name : filter) {
|
||||||
|
api.addParameter("q", name);
|
||||||
|
}
|
||||||
|
api.send(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AccountCapabilities() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native boolean canPerform(String name)
|
||||||
|
/*-{ return this[name] ? true : false; }-*/;
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
package com.google.gerrit.client.account;
|
package com.google.gerrit.client.account;
|
||||||
|
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.projects.ProjectMap;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.client.ui.HintTextBox;
|
import com.google.gerrit.client.ui.HintTextBox;
|
||||||
@@ -22,7 +23,6 @@ import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
|
|||||||
import com.google.gerrit.client.ui.ProjectsTable;
|
import com.google.gerrit.client.ui.ProjectsTable;
|
||||||
import com.google.gerrit.common.PageLinks;
|
import com.google.gerrit.common.PageLinks;
|
||||||
import com.google.gerrit.common.data.AccountProjectWatchInfo;
|
import com.google.gerrit.common.data.AccountProjectWatchInfo;
|
||||||
import com.google.gerrit.common.data.ProjectList;
|
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
import com.google.gwt.event.dom.client.ClickHandler;
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
import com.google.gwt.event.dom.client.KeyCodes;
|
import com.google.gwt.event.dom.client.KeyCodes;
|
||||||
@@ -226,14 +226,14 @@ public class MyWatchedProjectsScreen extends SettingsScreen implements
|
|||||||
|
|
||||||
// prevent user input from being overwritten by simply poping up
|
// prevent user input from being overwritten by simply poping up
|
||||||
if (! popingUp || "".equals(nameBox.getText()) ) {
|
if (! popingUp || "".equals(nameBox.getText()) ) {
|
||||||
nameBox.setText(getRowItem(row).getName());
|
nameBox.setText(getRowItem(row).name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onOpenRow(final int row) {
|
protected void onOpenRow(final int row) {
|
||||||
super.onOpenRow(row);
|
super.onOpenRow(row);
|
||||||
nameBox.setText(getRowItem(row).getName());
|
nameBox.setText(getRowItem(row).name());
|
||||||
doAddNew();
|
doAddNew();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -361,11 +361,10 @@ public class MyWatchedProjectsScreen extends SettingsScreen implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void populateProjects() {
|
protected void populateProjects() {
|
||||||
Util.PROJECT_SVC.visibleProjects(
|
ProjectMap.all(new GerritCallback<ProjectMap>() {
|
||||||
new GerritCallback<ProjectList>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final ProjectList result) {
|
public void onSuccess(final ProjectMap result) {
|
||||||
projectsTab.display(result.getProjects());
|
projectsTab.display(result);
|
||||||
if (firstPopupLoad) { // Display was delayed until table was loaded
|
if (firstPopupLoad) { // Display was delayed until table was loaded
|
||||||
firstPopupLoad = false;
|
firstPopupLoad = false;
|
||||||
displayPopup();
|
displayPopup();
|
||||||
|
@@ -17,6 +17,8 @@ package com.google.gerrit.client.admin;
|
|||||||
import com.google.gerrit.client.Dispatcher;
|
import com.google.gerrit.client.Dispatcher;
|
||||||
import com.google.gerrit.client.ErrorDialog;
|
import com.google.gerrit.client.ErrorDialog;
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.projects.ProjectInfo;
|
||||||
|
import com.google.gerrit.client.projects.ProjectMap;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.ui.HintTextBox;
|
import com.google.gerrit.client.ui.HintTextBox;
|
||||||
import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
|
import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
|
||||||
@@ -38,8 +40,6 @@ import com.google.gwt.user.client.ui.VerticalPanel;
|
|||||||
import com.google.gwtexpui.globalkey.client.NpTextBox;
|
import com.google.gwtexpui.globalkey.client.NpTextBox;
|
||||||
import com.google.gwtjsonrpc.common.VoidResult;
|
import com.google.gwtjsonrpc.common.VoidResult;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CreateProjectScreen extends Screen {
|
public class CreateProjectScreen extends Screen {
|
||||||
private NpTextBox project;
|
private NpTextBox project;
|
||||||
private Button create;
|
private Button create;
|
||||||
@@ -127,31 +127,30 @@ public class CreateProjectScreen extends Screen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populate(final int row, final Project k) {
|
protected void populate(final int row, final ProjectInfo k) {
|
||||||
final Anchor projectLink = new Anchor(k.getName());
|
final Anchor projectLink = new Anchor(k.name());
|
||||||
projectLink.addClickHandler(new ClickHandler() {
|
projectLink.addClickHandler(new ClickHandler() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(ClickEvent event) {
|
public void onClick(ClickEvent event) {
|
||||||
sugestParent.setText(getRowItem(row).getName());
|
sugestParent.setText(getRowItem(row).name());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
table.setWidget(row, 1, projectLink);
|
table.setWidget(row, 1, projectLink);
|
||||||
table.setText(row, 2, k.getDescription());
|
table.setText(row, 2, k.description());
|
||||||
|
|
||||||
setRowItem(row, k);
|
setRowItem(row, k);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
suggestedParentsTab.setVisible(false);
|
suggestedParentsTab.setVisible(false);
|
||||||
|
|
||||||
Util.PROJECT_SVC
|
ProjectMap.permissions(new GerritCallback<ProjectMap>() {
|
||||||
.suggestParentCandidates(new GerritCallback<List<Project>>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Project> result) {
|
public void onSuccess(ProjectMap list) {
|
||||||
if (result != null && !result.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
suggestedParentsTab.setVisible(true);
|
suggestedParentsTab.setVisible(true);
|
||||||
suggestedParentsTab.display(result);
|
suggestedParentsTab.display(list);
|
||||||
suggestedParentsTab.finishDisplay();
|
suggestedParentsTab.finishDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,8 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.admin;
|
package com.google.gerrit.client.admin;
|
||||||
|
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
|
||||||
|
|
||||||
import com.google.gerrit.client.Dispatcher;
|
import com.google.gerrit.client.Dispatcher;
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.account.AccountCapabilities;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.client.ui.AccountScreen;
|
import com.google.gerrit.client.ui.AccountScreen;
|
||||||
@@ -48,11 +51,17 @@ public class GroupListScreen extends AccountScreen {
|
|||||||
@Override
|
@Override
|
||||||
protected void onLoad() {
|
protected void onLoad() {
|
||||||
super.onLoad();
|
super.onLoad();
|
||||||
|
addPanel.setVisible(false);
|
||||||
|
AccountCapabilities.all(new GerritCallback<AccountCapabilities>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(AccountCapabilities ac) {
|
||||||
|
addPanel.setVisible(ac.canPerform(CREATE_GROUP));
|
||||||
|
}
|
||||||
|
}, CREATE_GROUP);
|
||||||
Util.GROUP_SVC
|
Util.GROUP_SVC
|
||||||
.visibleGroups(new ScreenLoadCallback<GroupList>(this) {
|
.visibleGroups(new ScreenLoadCallback<GroupList>(this) {
|
||||||
@Override
|
@Override
|
||||||
protected void preDisplay(GroupList result) {
|
protected void preDisplay(GroupList result) {
|
||||||
addPanel.setVisible(result.isCanCreateGroup());
|
|
||||||
groups.display(result.getGroups());
|
groups.display(result.getGroups());
|
||||||
groups.finishDisplay();
|
groups.finishDisplay();
|
||||||
}
|
}
|
||||||
|
@@ -14,15 +14,19 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.admin;
|
package com.google.gerrit.client.admin;
|
||||||
|
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
|
||||||
|
|
||||||
import com.google.gerrit.client.Dispatcher;
|
import com.google.gerrit.client.Dispatcher;
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
|
import com.google.gerrit.client.account.AccountCapabilities;
|
||||||
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
|
import com.google.gerrit.client.projects.ProjectInfo;
|
||||||
|
import com.google.gerrit.client.projects.ProjectMap;
|
||||||
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
import com.google.gerrit.client.rpc.ScreenLoadCallback;
|
||||||
import com.google.gerrit.client.ui.Hyperlink;
|
import com.google.gerrit.client.ui.Hyperlink;
|
||||||
import com.google.gerrit.client.ui.ProjectsTable;
|
import com.google.gerrit.client.ui.ProjectsTable;
|
||||||
import com.google.gerrit.client.ui.Screen;
|
import com.google.gerrit.client.ui.Screen;
|
||||||
import com.google.gerrit.common.PageLinks;
|
import com.google.gerrit.common.PageLinks;
|
||||||
import com.google.gerrit.common.data.ProjectList;
|
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
|
||||||
import com.google.gwt.user.client.History;
|
import com.google.gwt.user.client.History;
|
||||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||||
|
|
||||||
@@ -33,11 +37,17 @@ public class ProjectListScreen extends Screen {
|
|||||||
@Override
|
@Override
|
||||||
protected void onLoad() {
|
protected void onLoad() {
|
||||||
super.onLoad();
|
super.onLoad();
|
||||||
Util.PROJECT_SVC.visibleProjects(new ScreenLoadCallback<ProjectList>(this) {
|
createProjectLinkPanel.setVisible(false);
|
||||||
|
AccountCapabilities.all(new GerritCallback<AccountCapabilities>() {
|
||||||
@Override
|
@Override
|
||||||
protected void preDisplay(final ProjectList result) {
|
public void onSuccess(AccountCapabilities ac) {
|
||||||
createProjectLinkPanel.setVisible(result.canCreateProject());
|
createProjectLinkPanel.setVisible(ac.canPerform(CREATE_PROJECT));
|
||||||
projects.display(result.getProjects());
|
}
|
||||||
|
}, CREATE_PROJECT);
|
||||||
|
ProjectMap.all(new ScreenLoadCallback<ProjectMap>(this) {
|
||||||
|
@Override
|
||||||
|
protected void preDisplay(final ProjectMap result) {
|
||||||
|
projects.display(result);
|
||||||
projects.finishDisplay();
|
projects.finishDisplay();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -61,14 +71,14 @@ public class ProjectListScreen extends Screen {
|
|||||||
History.newItem(link(getRowItem(row)));
|
History.newItem(link(getRowItem(row)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String link(final Project item) {
|
private String link(final ProjectInfo item) {
|
||||||
return Dispatcher.toProjectAdmin(item.getNameKey(), ProjectScreen.INFO);
|
return Dispatcher.toProjectAdmin(item.name_key(), ProjectScreen.INFO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void populate(final int row, final Project k) {
|
protected void populate(final int row, final ProjectInfo k) {
|
||||||
table.setWidget(row, 1, new Hyperlink(k.getName(), link(k)));
|
table.setWidget(row, 1, new Hyperlink(k.name(), link(k)));
|
||||||
table.setText(row, 2, k.getDescription());
|
table.setText(row, 2, k.description());
|
||||||
|
|
||||||
setRowItem(row, k);
|
setRowItem(row, k);
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.projects;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwt.user.client.ui.SuggestOracle;
|
||||||
|
|
||||||
|
public class ProjectInfo
|
||||||
|
extends JavaScriptObject
|
||||||
|
implements SuggestOracle.Suggestion {
|
||||||
|
public final Project.NameKey name_key() {
|
||||||
|
return new Project.NameKey(name());
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native String name() /*-{ return this.name; }-*/;
|
||||||
|
public final native String description() /*-{ return this.description; }-*/;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getDisplayString() {
|
||||||
|
if (description() != null) {
|
||||||
|
return name() + " (" + description() + ")";
|
||||||
|
}
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getReplacementString() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProjectInfo() {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.projects;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
|
import com.google.gerrit.client.rpc.RestApi;
|
||||||
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
|
import com.google.gwt.http.client.URL;
|
||||||
|
|
||||||
|
/** Projects available from {@code /projects/}. */
|
||||||
|
public class ProjectMap extends NativeMap<ProjectInfo> {
|
||||||
|
public static void all(AsyncCallback<ProjectMap> callback) {
|
||||||
|
new RestApi("/projects/")
|
||||||
|
.addParameterRaw("type", "ALL")
|
||||||
|
.addParameterTrue("all")
|
||||||
|
.addParameterTrue("d") // description
|
||||||
|
.send(NativeMap.copyKeysIntoChildren(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void permissions(AsyncCallback<ProjectMap> callback) {
|
||||||
|
new RestApi("/projects/")
|
||||||
|
.addParameterRaw("type", "PERMISSIONS")
|
||||||
|
.addParameterTrue("all")
|
||||||
|
.addParameterTrue("d") // description
|
||||||
|
.send(NativeMap.copyKeysIntoChildren(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void suggest(String prefix, int limit, AsyncCallback<ProjectMap> cb) {
|
||||||
|
new RestApi("/projects/" + URL.encode(prefix).replaceAll("[?]", "%3F"))
|
||||||
|
.addParameterRaw("type", "ALL")
|
||||||
|
.addParameter("n", limit)
|
||||||
|
.addParameterTrue("d") // description
|
||||||
|
.send(NativeMap.copyKeysIntoChildren(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ProjectMap() {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** A read-only list of native JavaScript objects stored in a JSON array. */
|
||||||
|
public class NativeList<T extends JavaScriptObject> extends JavaScriptObject {
|
||||||
|
protected NativeList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final List<T> asList() {
|
||||||
|
return new AbstractList<T>() {
|
||||||
|
@Override
|
||||||
|
public T set(int index, T element) {
|
||||||
|
T old = NativeList.this.get(index);
|
||||||
|
NativeList.this.set0(index, element);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get(int index) {
|
||||||
|
return NativeList.this.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return NativeList.this.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native int size() /*-{ return this.length; }-*/;
|
||||||
|
public final native T get(int i) /*-{ return this[i]; }-*/;
|
||||||
|
private final native void set0(int i, T v) /*-{ this[i] = v; }-*/;
|
||||||
|
}
|
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/** A map of native JSON objects, keyed by a string. */
|
||||||
|
public class NativeMap<T extends JavaScriptObject> extends JavaScriptObject {
|
||||||
|
/**
|
||||||
|
* Loop through the result map's entries and copy the key strings into the
|
||||||
|
* "name" property of the corresponding child object. This only runs on the
|
||||||
|
* top level map of the result, and requires the children to be JSON objects
|
||||||
|
* and not a JSON primitive (e.g. boolean or string).
|
||||||
|
*/
|
||||||
|
public static <T extends JavaScriptObject,
|
||||||
|
M extends NativeMap<T>> AsyncCallback<M> copyKeysIntoChildren(
|
||||||
|
AsyncCallback<M> callback) {
|
||||||
|
return copyKeysIntoChildren("name", callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loop through the result map and set asProperty on the children. */
|
||||||
|
public static <T extends JavaScriptObject,
|
||||||
|
M extends NativeMap<T>> AsyncCallback<M> copyKeysIntoChildren(
|
||||||
|
final String asProperty, AsyncCallback<M> callback) {
|
||||||
|
return new TransformCallback<M, M>(callback) {
|
||||||
|
@Override
|
||||||
|
protected M transform(M result) {
|
||||||
|
result.copyKeysIntoChildren(asProperty);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NativeMap() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Set<String> keySet() {
|
||||||
|
return Natives.keys(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native NativeList<T> values()
|
||||||
|
/*-{
|
||||||
|
var s = this;
|
||||||
|
var v = [];
|
||||||
|
var i = 0;
|
||||||
|
for (var k in s) {
|
||||||
|
if (s.hasOwnProperty(k)) {
|
||||||
|
v[i++] = s[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
public final int size() {
|
||||||
|
return keySet().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean containsKey(String n) {
|
||||||
|
return get(n) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final native T get(String n) /*-{ return this[n]; }-*/;
|
||||||
|
|
||||||
|
public final native void copyKeysIntoChildren(String p)
|
||||||
|
/*-{
|
||||||
|
var s = this;
|
||||||
|
for (var k in s) {
|
||||||
|
if (s.hasOwnProperty(k)) {
|
||||||
|
var c = s[k];
|
||||||
|
c[p] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}-*/;
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwt.json.client.JSONObject;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Natives {
|
||||||
|
/**
|
||||||
|
* Get the names of defined properties on the object. The returned set
|
||||||
|
* iterates in the native iteration order, which may match the source order.
|
||||||
|
*/
|
||||||
|
public static Set<String> keys(JavaScriptObject obj) {
|
||||||
|
if (obj != null) {
|
||||||
|
return new JSONObject(obj).keySet();
|
||||||
|
}
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends JavaScriptObject> T parseJSON(String json) {
|
||||||
|
if (parser == null) {
|
||||||
|
parser = bestJsonParser();
|
||||||
|
}
|
||||||
|
return parse0(parser, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native <T extends JavaScriptObject>
|
||||||
|
T parse0(JavaScriptObject p, String s)
|
||||||
|
/*-{ return p(s); }-*/;
|
||||||
|
|
||||||
|
private static JavaScriptObject parser;
|
||||||
|
private static native JavaScriptObject bestJsonParser()
|
||||||
|
/*-{
|
||||||
|
if ($wnd.JSON && typeof $wnd.JSON.parse === 'function')
|
||||||
|
return $wnd.JSON.parse;
|
||||||
|
return function(s) { return eval('(' + s + ')'); };
|
||||||
|
}-*/;
|
||||||
|
|
||||||
|
private Natives() {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,174 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.RpcStatus;
|
||||||
|
import com.google.gwt.core.client.GWT;
|
||||||
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
|
import com.google.gwt.http.client.Request;
|
||||||
|
import com.google.gwt.http.client.RequestBuilder;
|
||||||
|
import com.google.gwt.http.client.RequestCallback;
|
||||||
|
import com.google.gwt.http.client.RequestException;
|
||||||
|
import com.google.gwt.http.client.Response;
|
||||||
|
import com.google.gwt.http.client.URL;
|
||||||
|
import com.google.gwt.user.client.rpc.StatusCodeException;
|
||||||
|
import com.google.gwtjsonrpc.client.RemoteJsonException;
|
||||||
|
import com.google.gwtjsonrpc.client.ServerUnavailableException;
|
||||||
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
|
import com.google.gwtjsonrpc.common.JsonConstants;
|
||||||
|
|
||||||
|
/** Makes a REST API call to the server. */
|
||||||
|
public class RestApi {
|
||||||
|
/**
|
||||||
|
* Expected JSON content body prefix that prevents XSSI.
|
||||||
|
* <p>
|
||||||
|
* The server always includes this line as the first line of the response
|
||||||
|
* content body when the response body is formatted as JSON. It gets inserted
|
||||||
|
* by the server to prevent the resource from being imported into another
|
||||||
|
* domain's page using a <script> tag. This line must be removed before
|
||||||
|
* the JSON can be parsed.
|
||||||
|
*/
|
||||||
|
private static final String JSON_MAGIC = ")]}'\n";
|
||||||
|
|
||||||
|
private StringBuilder url;
|
||||||
|
private boolean hasQueryParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a new API call.
|
||||||
|
* <p>
|
||||||
|
* By default the JSON format will be selected by including an HTTP Accept
|
||||||
|
* header in the request.
|
||||||
|
*
|
||||||
|
* @param name URL of the REST resource to access, e.g. {@code "/projects/"}
|
||||||
|
* to list accessible projects from the server.
|
||||||
|
*/
|
||||||
|
public RestApi(String name) {
|
||||||
|
if (name.startsWith("/")) {
|
||||||
|
name = name.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
url = new StringBuilder();
|
||||||
|
url.append(GWT.getHostPageBaseURL());
|
||||||
|
url.append(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestApi addParameter(String name, String value) {
|
||||||
|
return addParameterRaw(name, URL.encodeQueryString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestApi addParameterTrue(String name) {
|
||||||
|
return addParameterRaw(name, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestApi addParameter(String name, boolean value) {
|
||||||
|
return addParameterRaw(name, value ? "t" : "f");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestApi addParameter(String name, int value) {
|
||||||
|
return addParameterRaw(name, String.valueOf(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestApi addParameterRaw(String name, String value) {
|
||||||
|
if (hasQueryParams) {
|
||||||
|
url.append("&");
|
||||||
|
} else {
|
||||||
|
url.append("?");
|
||||||
|
hasQueryParams = true;
|
||||||
|
}
|
||||||
|
url.append(name);
|
||||||
|
if (value != null) {
|
||||||
|
url.append("=").append(value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends JavaScriptObject> void send(final AsyncCallback<T> cb) {
|
||||||
|
RequestBuilder req = new RequestBuilder(RequestBuilder.GET, url.toString());
|
||||||
|
req.setHeader("Accept", JsonConstants.JSON_TYPE);
|
||||||
|
req.setCallback(new RequestCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResponseReceived(Request req, Response res) {
|
||||||
|
RpcStatus.INSTANCE.onRpcComplete();
|
||||||
|
int status = res.getStatusCode();
|
||||||
|
if (status != 200) {
|
||||||
|
if ((400 <= status && status < 500) && isTextBody(res)) {
|
||||||
|
cb.onFailure(new RemoteJsonException(res.getText(), status, null));
|
||||||
|
} else {
|
||||||
|
cb.onFailure(new StatusCodeException(status, res.getStatusText()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isJsonBody(res)) {
|
||||||
|
cb.onFailure(new RemoteJsonException("Invalid JSON"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String json = res.getText();
|
||||||
|
if (!json.startsWith(JSON_MAGIC)) {
|
||||||
|
cb.onFailure(new RemoteJsonException("Invalid JSON"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T data;
|
||||||
|
try {
|
||||||
|
data = Natives.parseJSON(json.substring(JSON_MAGIC.length()));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
cb.onFailure(new RemoteJsonException("Invalid JSON"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb.onSuccess(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Request req, Throwable err) {
|
||||||
|
RpcStatus.INSTANCE.onRpcComplete();
|
||||||
|
if (err.getMessage().contains("XmlHttpRequest.status")) {
|
||||||
|
cb.onFailure(new ServerUnavailableException());
|
||||||
|
} else {
|
||||||
|
cb.onFailure(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
RpcStatus.INSTANCE.onRpcStart();
|
||||||
|
req.send();
|
||||||
|
} catch (RequestException e) {
|
||||||
|
RpcStatus.INSTANCE.onRpcComplete();
|
||||||
|
cb.onFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isJsonBody(Response res) {
|
||||||
|
return isContentType(res, JsonConstants.JSON_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTextBody(Response res) {
|
||||||
|
return isContentType(res, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isContentType(Response res, String want) {
|
||||||
|
String type = res.getHeader("Content-Type");
|
||||||
|
if (type == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int semi = type.indexOf(';');
|
||||||
|
if (semi >= 0) {
|
||||||
|
type = type.substring(0, semi).trim();
|
||||||
|
}
|
||||||
|
return want.equals(type);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
|
|
||||||
|
/** Transforms a value and passes it on to another callback. */
|
||||||
|
public abstract class TransformCallback<I, O> implements AsyncCallback<I>{
|
||||||
|
private final AsyncCallback<O> callback;
|
||||||
|
|
||||||
|
protected TransformCallback(AsyncCallback<O> callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(I result) {
|
||||||
|
callback.onSuccess(transform(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable caught) {
|
||||||
|
callback.onFailure(caught);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract O transform(I result);
|
||||||
|
}
|
@@ -15,49 +15,25 @@
|
|||||||
package com.google.gerrit.client.ui;
|
package com.google.gerrit.client.ui;
|
||||||
|
|
||||||
import com.google.gerrit.client.RpcStatus;
|
import com.google.gerrit.client.RpcStatus;
|
||||||
|
import com.google.gerrit.client.projects.ProjectMap;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
|
||||||
import com.google.gwt.user.client.ui.SuggestOracle;
|
|
||||||
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
|
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Suggestion Oracle for Project.NameKey entities. */
|
/** Suggestion Oracle for Project.NameKey entities. */
|
||||||
public class ProjectNameSuggestOracle extends HighlightSuggestOracle {
|
public class ProjectNameSuggestOracle extends HighlightSuggestOracle {
|
||||||
@Override
|
@Override
|
||||||
public void onRequestSuggestions(final Request req, final Callback callback) {
|
public void onRequestSuggestions(final Request req, final Callback callback) {
|
||||||
RpcStatus.hide(new Runnable() {
|
RpcStatus.hide(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
SuggestUtil.SVC.suggestProjectNameKey(req.getQuery(), req.getLimit(),
|
ProjectMap.suggest(req.getQuery(), req.getLimit(),
|
||||||
new GerritCallback<List<Project.NameKey>>() {
|
new GerritCallback<ProjectMap>() {
|
||||||
public void onSuccess(final List<Project.NameKey> result) {
|
@Override
|
||||||
final ArrayList<ProjectNameSuggestion> r =
|
public void onSuccess(ProjectMap map) {
|
||||||
new ArrayList<ProjectNameSuggestion>(result.size());
|
callback.onSuggestionsReady(req, new Response(map.values().asList()));
|
||||||
for (final Project.NameKey p : result) {
|
|
||||||
r.add(new ProjectNameSuggestion(p));
|
|
||||||
}
|
|
||||||
callback.onSuggestionsReady(req, new Response(r));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ProjectNameSuggestion implements
|
|
||||||
SuggestOracle.Suggestion {
|
|
||||||
private final Project.NameKey key;
|
|
||||||
|
|
||||||
ProjectNameSuggestion(final Project.NameKey k) {
|
|
||||||
key = k;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayString() {
|
|
||||||
return key.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReplacementString() {
|
|
||||||
return key.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -15,16 +15,19 @@
|
|||||||
package com.google.gerrit.client.ui;
|
package com.google.gerrit.client.ui;
|
||||||
|
|
||||||
import com.google.gerrit.client.Gerrit;
|
import com.google.gerrit.client.Gerrit;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.client.projects.ProjectInfo;
|
||||||
|
import com.google.gerrit.client.projects.ProjectMap;
|
||||||
import com.google.gwt.event.dom.client.KeyCodes;
|
import com.google.gwt.event.dom.client.KeyCodes;
|
||||||
import com.google.gwt.user.client.DOM;
|
import com.google.gwt.user.client.DOM;
|
||||||
import com.google.gwt.user.client.Element;
|
import com.google.gwt.user.client.Element;
|
||||||
import com.google.gwt.user.client.Event;
|
import com.google.gwt.user.client.Event;
|
||||||
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ProjectsTable extends NavigationTable<Project> {
|
public class ProjectsTable extends NavigationTable<ProjectInfo> {
|
||||||
|
|
||||||
public ProjectsTable() {
|
public ProjectsTable() {
|
||||||
keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.projectListPrev()));
|
keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.projectListPrev()));
|
||||||
@@ -41,6 +44,7 @@ public class ProjectsTable extends NavigationTable<Project> {
|
|||||||
fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
|
fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected MyFlexTable createFlexTable() {
|
protected MyFlexTable createFlexTable() {
|
||||||
MyFlexTable table = new MyFlexTable() {
|
MyFlexTable table = new MyFlexTable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -78,8 +82,8 @@ public class ProjectsTable extends NavigationTable<Project> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object getRowItemKey(final Project item) {
|
protected Object getRowItemKey(final ProjectInfo item) {
|
||||||
return item.getNameKey();
|
return item.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -89,17 +93,24 @@ public class ProjectsTable extends NavigationTable<Project> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void display(final List<Project> projects) {
|
public void display(ProjectMap projects) {
|
||||||
while (1 < table.getRowCount())
|
while (1 < table.getRowCount())
|
||||||
table.removeRow(table.getRowCount() - 1);
|
table.removeRow(table.getRowCount() - 1);
|
||||||
|
|
||||||
for (final Project k : projects)
|
List<ProjectInfo> list = projects.values().asList();
|
||||||
insert(table.getRowCount(), k);
|
Collections.sort(list, new Comparator<ProjectInfo>() {
|
||||||
|
@Override
|
||||||
|
public int compare(ProjectInfo a, ProjectInfo b) {
|
||||||
|
return a.name().compareTo(b.name());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for(ProjectInfo p : list)
|
||||||
|
insert(table.getRowCount(), p);
|
||||||
|
|
||||||
finishDisplay();
|
finishDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void insert(final int row, final Project k) {
|
protected void insert(final int row, final ProjectInfo k) {
|
||||||
table.insertRow(row);
|
table.insertRow(row);
|
||||||
|
|
||||||
applyDataRowStyle(row);
|
applyDataRowStyle(row);
|
||||||
@@ -112,9 +123,9 @@ public class ProjectsTable extends NavigationTable<Project> {
|
|||||||
populate(row, k);
|
populate(row, k);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void populate(final int row, final Project k) {
|
protected void populate(final int row, final ProjectInfo k) {
|
||||||
table.setText(row, 1, k.getName());
|
table.setText(row, 1, k.name());
|
||||||
table.setText(row, 2, k.getDescription());
|
table.setText(row, 2, k.description());
|
||||||
|
|
||||||
setRowItem(row, k);
|
setRowItem(row, k);
|
||||||
}
|
}
|
||||||
|
@@ -20,12 +20,10 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
|||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gwtjsonrpc.server.XsrfException;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -39,7 +37,6 @@ import javax.servlet.ServletRequest;
|
|||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.servlet.http.HttpServletResponseWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trust the authentication which is done by the container.
|
* Trust the authentication which is done by the container.
|
||||||
@@ -62,7 +59,7 @@ class ContainerAuthFilter implements Filter {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache,
|
ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache,
|
||||||
@GerritServerConfig Config config) throws XsrfException {
|
@GerritServerConfig Config config) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@@ -80,20 +77,14 @@ class ContainerAuthFilter implements Filter {
|
|||||||
public void doFilter(ServletRequest request, ServletResponse response,
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
FilterChain chain) throws IOException, ServletException {
|
FilterChain chain) throws IOException, ServletException {
|
||||||
HttpServletRequest req = (HttpServletRequest) request;
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
if (!GitSmartHttpTools.isGitClient(req)) {
|
HttpServletResponse rsp = (HttpServletResponse) response;
|
||||||
chain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpServletResponseWrapper rsp =
|
|
||||||
new HttpServletResponseWrapper((HttpServletResponse) response);
|
|
||||||
|
|
||||||
if (verify(req, rsp)) {
|
if (verify(req, rsp)) {
|
||||||
chain.doFilter(req, response);
|
chain.doFilter(req, response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp)
|
private boolean verify(HttpServletRequest req, HttpServletResponse rsp)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
String username = req.getRemoteUser();
|
String username = req.getRemoteUser();
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
|
@@ -41,5 +41,7 @@ public class GitOverHttpModule extends ServletModule {
|
|||||||
String git = GitOverHttpServlet.URL_REGEX;
|
String git = GitOverHttpServlet.URL_REGEX;
|
||||||
filterRegex(git).through(authFilter);
|
filterRegex(git).through(authFilter);
|
||||||
serveRegex(git).with(GitOverHttpServlet.class);
|
serveRegex(git).with(GitOverHttpServlet.class);
|
||||||
|
|
||||||
|
filter("/a/*").through(authFilter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,6 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -100,11 +99,6 @@ class ProjectDigestFilter implements Filter {
|
|||||||
public void doFilter(ServletRequest request, ServletResponse response,
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
FilterChain chain) throws IOException, ServletException {
|
FilterChain chain) throws IOException, ServletException {
|
||||||
HttpServletRequest req = (HttpServletRequest) request;
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
if (!GitSmartHttpTools.isGitClient(req)) {
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Response rsp = new Response(req, (HttpServletResponse) response);
|
Response rsp = new Response(req, (HttpServletResponse) response);
|
||||||
|
|
||||||
if (verify(req, rsp)) {
|
if (verify(req, rsp)) {
|
||||||
|
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.httpd;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/** Requires the user to be authenticated over HTTP. */
|
||||||
|
@Singleton
|
||||||
|
class RequireIdentifiedUserFilter implements Filter {
|
||||||
|
private final Provider<CurrentUser> user;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
RequireIdentifiedUserFilter(Provider<CurrentUser> user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request,
|
||||||
|
ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (user.get() instanceof IdentifiedUser) {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
} else {
|
||||||
|
HttpServletResponse res = (HttpServletResponse) response;
|
||||||
|
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,178 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.httpd;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.gerrit.util.cli.CmdLineParser;
|
||||||
|
import com.google.gwtjsonrpc.server.RPCServletUtils;
|
||||||
|
import com.google.gwtjsonrpc.common.JsonConstants;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
import org.kohsuke.args4j.CmdLineException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
public abstract class RestApiServlet extends HttpServlet {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final Logger log =
|
||||||
|
LoggerFactory.getLogger(RestApiServlet.class);
|
||||||
|
|
||||||
|
/** MIME type used for a JSON response body. */
|
||||||
|
protected static final String JSON_TYPE = JsonConstants.JSON_TYPE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Garbage prefix inserted before JSON output to prevent XSSI.
|
||||||
|
* <p>
|
||||||
|
* This prefix is ")]}'\n" and is designed to prevent a web browser from
|
||||||
|
* executing the response body if the resource URI were to be referenced using
|
||||||
|
* a <script src="...> HTML tag from another web site. Clients using the
|
||||||
|
* HTTP interface will need to always strip the first line of response data to
|
||||||
|
* remove this magic header.
|
||||||
|
*/
|
||||||
|
protected static final byte[] JSON_MAGIC;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
JSON_MAGIC = ")]}'\n".getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException("UTF-8 not supported", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
noCache(res);
|
||||||
|
try {
|
||||||
|
super.service(req, res);
|
||||||
|
} catch (Error err) {
|
||||||
|
handleError(err, req, res);
|
||||||
|
} catch (RuntimeException err) {
|
||||||
|
handleError(err, req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void noCache(HttpServletResponse res) {
|
||||||
|
res.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
|
||||||
|
res.setHeader("Pragma", "no-cache");
|
||||||
|
res.setHeader("Cache-Control", "no-cache, must-revalidate");
|
||||||
|
res.setHeader("Content-Disposition", "attachment");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleError(
|
||||||
|
Throwable err, HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws IOException {
|
||||||
|
String uri = req.getRequestURI();
|
||||||
|
if (!Strings.isNullOrEmpty(req.getQueryString())) {
|
||||||
|
uri += "?" + req.getQueryString();
|
||||||
|
}
|
||||||
|
log.error(String.format("Error in %s %s", req.getMethod(), uri), err);
|
||||||
|
|
||||||
|
if (!res.isCommitted()) {
|
||||||
|
res.reset();
|
||||||
|
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
|
noCache(res);
|
||||||
|
sendText(req, res, "Internal Server Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean acceptsJson(HttpServletRequest req) {
|
||||||
|
String accept = req.getHeader("Accept");
|
||||||
|
if (accept == null) {
|
||||||
|
return false;
|
||||||
|
} else if (JSON_TYPE.equals(accept)) {
|
||||||
|
return true;
|
||||||
|
} else if (accept.startsWith(JSON_TYPE + ",")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (String p : accept.split("[ ,;][ ,;]*")) {
|
||||||
|
if (JSON_TYPE.equals(p)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void sendText(HttpServletRequest req,
|
||||||
|
HttpServletResponse res, String data) throws IOException {
|
||||||
|
res.setContentType("text/plain");
|
||||||
|
res.setCharacterEncoding("UTF-8");
|
||||||
|
send(req, res, data.getBytes("UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void send(HttpServletRequest req, HttpServletResponse res,
|
||||||
|
byte[] data) throws IOException {
|
||||||
|
if (data.length > 256 && RPCServletUtils.acceptsGzipEncoding(req)) {
|
||||||
|
res.setHeader("Content-Encoding", "gzip");
|
||||||
|
data = HtmlDomUtil.compress(data);
|
||||||
|
}
|
||||||
|
res.setContentLength(data.length);
|
||||||
|
OutputStream out = res.getOutputStream();
|
||||||
|
try {
|
||||||
|
out.write(data);
|
||||||
|
} finally {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ParameterParser {
|
||||||
|
private final CmdLineParser.Factory parserFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ParameterParser(CmdLineParser.Factory pf) {
|
||||||
|
this.parserFactory = pf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean parse(T param, HttpServletRequest req,
|
||||||
|
HttpServletResponse res) throws IOException {
|
||||||
|
CmdLineParser clp = parserFactory.create(param);
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, String[]> parameterMap = req.getParameterMap();
|
||||||
|
clp.parseOptionMap(parameterMap);
|
||||||
|
} catch (CmdLineException e) {
|
||||||
|
if (!clp.wasHelpRequestedByOption()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
sendText(req, res, e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clp.wasHelpRequestedByOption()) {
|
||||||
|
StringWriter msg = new StringWriter();
|
||||||
|
clp.printQueryStringUsage(req.getRequestURI(), msg);
|
||||||
|
msg.write('\n');
|
||||||
|
msg.write('\n');
|
||||||
|
clp.printUsage(msg, null);
|
||||||
|
msg.write('\n');
|
||||||
|
sendText(req, res, msg.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -23,6 +23,8 @@ import com.google.gerrit.httpd.raw.LegacyGerritServlet;
|
|||||||
import com.google.gerrit.httpd.raw.SshInfoServlet;
|
import com.google.gerrit.httpd.raw.SshInfoServlet;
|
||||||
import com.google.gerrit.httpd.raw.StaticServlet;
|
import com.google.gerrit.httpd.raw.StaticServlet;
|
||||||
import com.google.gerrit.httpd.raw.ToolServlet;
|
import com.google.gerrit.httpd.raw.ToolServlet;
|
||||||
|
import com.google.gerrit.httpd.rpc.account.AccountCapabilitiesServlet;
|
||||||
|
import com.google.gerrit.httpd.rpc.project.ListProjectsServlet;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
import com.google.gerrit.reviewdb.client.Change;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gwtexpui.server.CacheControlFilter;
|
import com.google.gwtexpui.server.CacheControlFilter;
|
||||||
@@ -69,6 +71,10 @@ class UrlModule extends ServletModule {
|
|||||||
serveRegex("^/([1-9][0-9]*)/?$").with(directChangeById());
|
serveRegex("^/([1-9][0-9]*)/?$").with(directChangeById());
|
||||||
serveRegex("^/p/(.*)$").with(queryProjectNew());
|
serveRegex("^/p/(.*)$").with(queryProjectNew());
|
||||||
serveRegex("^/r/(.+)/?$").with(DirectChangeByCommit.class);
|
serveRegex("^/r/(.+)/?$").with(DirectChangeByCommit.class);
|
||||||
|
|
||||||
|
filter("/a/*").through(RequireIdentifiedUserFilter.class);
|
||||||
|
serveRegex("^/(?:a/)?accounts/self/capabilities$").with(AccountCapabilitiesServlet.class);
|
||||||
|
serveRegex("^/(?:a/)?projects/(.*)?$").with(ListProjectsServlet.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Key<HttpServlet> notFound() {
|
private Key<HttpServlet> notFound() {
|
||||||
|
@@ -38,6 +38,7 @@ import com.google.gerrit.server.contact.ContactStore;
|
|||||||
import com.google.gerrit.server.contact.ContactStoreProvider;
|
import com.google.gerrit.server.contact.ContactStoreProvider;
|
||||||
import com.google.gerrit.server.util.GuiceRequestScopePropagator;
|
import com.google.gerrit.server.util.GuiceRequestScopePropagator;
|
||||||
import com.google.gerrit.server.util.RequestScopePropagator;
|
import com.google.gerrit.server.util.RequestScopePropagator;
|
||||||
|
import com.google.gerrit.util.cli.CmdLineParser;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
@@ -135,6 +136,7 @@ public class WebModule extends FactoryModule {
|
|||||||
bind(ChangeUserName.CurrentUser.class);
|
bind(ChangeUserName.CurrentUser.class);
|
||||||
factory(ChangeUserName.Factory.class);
|
factory(ChangeUserName.Factory.class);
|
||||||
factory(ClearPassword.Factory.class);
|
factory(ClearPassword.Factory.class);
|
||||||
|
factory(CmdLineParser.Factory.class);
|
||||||
factory(GeneratePassword.Factory.class);
|
factory(GeneratePassword.Factory.class);
|
||||||
|
|
||||||
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
|
||||||
|
@@ -39,8 +39,6 @@ import com.google.gerrit.server.patch.AddReviewer;
|
|||||||
import com.google.gerrit.server.project.ChangeControl;
|
import com.google.gerrit.server.project.ChangeControl;
|
||||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
|
||||||
import com.google.gwtjsonrpc.common.AsyncCallback;
|
import com.google.gwtjsonrpc.common.AsyncCallback;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -60,8 +58,6 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
|
|||||||
private static final String MAX_SUFFIX = "\u9fa5";
|
private static final String MAX_SUFFIX = "\u9fa5";
|
||||||
|
|
||||||
private final Provider<ReviewDb> reviewDbProvider;
|
private final Provider<ReviewDb> reviewDbProvider;
|
||||||
private final ProjectControl.Factory projectControlFactory;
|
|
||||||
private final ProjectCache projectCache;
|
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
private final GroupControl.Factory groupControlFactory;
|
private final GroupControl.Factory groupControlFactory;
|
||||||
private final GroupMembers.Factory groupMembersFactory;
|
private final GroupMembers.Factory groupMembersFactory;
|
||||||
@@ -74,8 +70,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SuggestServiceImpl(final Provider<ReviewDb> schema,
|
SuggestServiceImpl(final Provider<ReviewDb> schema,
|
||||||
final ProjectControl.Factory projectControlFactory,
|
final AccountCache accountCache,
|
||||||
final ProjectCache projectCache, final AccountCache accountCache,
|
|
||||||
final GroupControl.Factory groupControlFactory,
|
final GroupControl.Factory groupControlFactory,
|
||||||
final GroupMembers.Factory groupMembersFactory,
|
final GroupMembers.Factory groupMembersFactory,
|
||||||
final Provider<CurrentUser> currentUser,
|
final Provider<CurrentUser> currentUser,
|
||||||
@@ -85,8 +80,6 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
|
|||||||
@GerritServerConfig final Config cfg, final GroupCache groupCache) {
|
@GerritServerConfig final Config cfg, final GroupCache groupCache) {
|
||||||
super(schema, currentUser);
|
super(schema, currentUser);
|
||||||
this.reviewDbProvider = schema;
|
this.reviewDbProvider = schema;
|
||||||
this.projectControlFactory = projectControlFactory;
|
|
||||||
this.projectCache = projectCache;
|
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
this.groupControlFactory = groupControlFactory;
|
this.groupControlFactory = groupControlFactory;
|
||||||
this.groupMembersFactory = groupMembersFactory;
|
this.groupMembersFactory = groupMembersFactory;
|
||||||
@@ -111,28 +104,6 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void suggestProjectNameKey(final String query, final int limit,
|
|
||||||
final AsyncCallback<List<Project.NameKey>> callback) {
|
|
||||||
final int max = 10;
|
|
||||||
final int n = limit <= 0 ? max : Math.min(limit, max);
|
|
||||||
|
|
||||||
final List<Project.NameKey> r = new ArrayList<Project.NameKey>(n);
|
|
||||||
for (final Project.NameKey nameKey : projectCache.byName(query)) {
|
|
||||||
final ProjectControl ctl;
|
|
||||||
try {
|
|
||||||
ctl = projectControlFactory.validateFor(nameKey);
|
|
||||||
} catch (NoSuchProjectException e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.add(ctl.getProject().getNameKey());
|
|
||||||
if (r.size() == n) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback.onSuccess(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface VisibilityControl {
|
private interface VisibilityControl {
|
||||||
boolean isVisible(Account account) throws OrmException;
|
boolean isVisible(Account account) throws OrmException;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,188 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.httpd.rpc.account;
|
||||||
|
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.CREATE_ACCOUNT;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.FLUSH_CACHES;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.KILL_TASK;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.PRIORITY;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.START_REPLICATION;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CONNECTIONS;
|
||||||
|
import static com.google.gerrit.common.data.GlobalCapability.VIEW_QUEUE;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
|
import com.google.gerrit.common.data.PermissionRange;
|
||||||
|
import com.google.gerrit.httpd.RestApiServlet;
|
||||||
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.OutputFormat;
|
||||||
|
import com.google.gerrit.server.account.CapabilityControl;
|
||||||
|
import com.google.gerrit.server.git.QueueProvider;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class AccountCapabilitiesServlet extends RestApiServlet {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final ParameterParser paramParser;
|
||||||
|
private final Provider<Impl> factory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountCapabilitiesServlet(
|
||||||
|
ParameterParser paramParser, Provider<Impl> factory) {
|
||||||
|
this.paramParser = paramParser;
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws IOException {
|
||||||
|
Impl impl = factory.get();
|
||||||
|
if (acceptsJson(req)) {
|
||||||
|
impl.format = OutputFormat.JSON_COMPACT;
|
||||||
|
}
|
||||||
|
if (paramParser.parse(impl, req, res)) {
|
||||||
|
impl.compute();
|
||||||
|
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
OutputStreamWriter out = new OutputStreamWriter(buf, "UTF-8");
|
||||||
|
if (impl.format.isJson()) {
|
||||||
|
res.setContentType(JSON_TYPE);
|
||||||
|
buf.write(JSON_MAGIC);
|
||||||
|
impl.format.newGson().toJson(
|
||||||
|
impl.have,
|
||||||
|
new TypeToken<Map<String, Object>>() {}.getType(),
|
||||||
|
out);
|
||||||
|
out.flush();
|
||||||
|
buf.write('\n');
|
||||||
|
} else {
|
||||||
|
res.setContentType("text/plain");
|
||||||
|
for (Map.Entry<String, Object> e : impl.have.entrySet()) {
|
||||||
|
out.write(e.getKey());
|
||||||
|
if (!(e.getValue() instanceof Boolean)) {
|
||||||
|
out.write(": ");
|
||||||
|
out.write(e.getValue().toString());
|
||||||
|
}
|
||||||
|
out.write('\n');
|
||||||
|
}
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
res.setCharacterEncoding("UTF-8");
|
||||||
|
send(req, res, buf.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Impl {
|
||||||
|
private final CapabilityControl cc;
|
||||||
|
private final Map<String, Object> have;
|
||||||
|
|
||||||
|
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
|
||||||
|
private OutputFormat format = OutputFormat.TEXT;
|
||||||
|
|
||||||
|
@Option(name = "-q", metaVar = "CAP", multiValued = true, usage = "Capability to inspect")
|
||||||
|
void addQuery(String name) {
|
||||||
|
if (query == null) {
|
||||||
|
query = Sets.newHashSet();
|
||||||
|
}
|
||||||
|
query.add(name.toLowerCase());
|
||||||
|
}
|
||||||
|
private Set<String> query;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Impl(CurrentUser user) {
|
||||||
|
cc = user.getCapabilities();
|
||||||
|
have = Maps.newLinkedHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute() {
|
||||||
|
for (String name : GlobalCapability.getAllNames()) {
|
||||||
|
if (!name.equals(PRIORITY) && want(name) && cc.canPerform(name)) {
|
||||||
|
if (GlobalCapability.hasRange(name)) {
|
||||||
|
have.put(name, new Range(cc.getRange(name)));
|
||||||
|
} else {
|
||||||
|
have.put(name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
have.put(CREATE_ACCOUNT, cc.canCreateAccount());
|
||||||
|
have.put(CREATE_GROUP, cc.canCreateGroup());
|
||||||
|
have.put(CREATE_PROJECT, cc.canCreateProject());
|
||||||
|
have.put(KILL_TASK, cc.canKillTask());
|
||||||
|
have.put(VIEW_CACHES, cc.canViewCaches());
|
||||||
|
have.put(FLUSH_CACHES, cc.canFlushCaches());
|
||||||
|
have.put(VIEW_CONNECTIONS, cc.canViewConnections());
|
||||||
|
have.put(VIEW_QUEUE, cc.canViewQueue());
|
||||||
|
have.put(START_REPLICATION, cc.canStartReplication());
|
||||||
|
|
||||||
|
QueueProvider.QueueType queue = cc.getQueueType();
|
||||||
|
if (queue != QueueProvider.QueueType.INTERACTIVE
|
||||||
|
|| (query != null && query.contains(PRIORITY))) {
|
||||||
|
have.put(PRIORITY, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Map.Entry<String, Object>> itr = have.entrySet().iterator();
|
||||||
|
while (itr.hasNext()) {
|
||||||
|
Map.Entry<String, Object> e = itr.next();
|
||||||
|
if (!want(e.getKey())) {
|
||||||
|
itr.remove();
|
||||||
|
} else if (e.getValue() instanceof Boolean && !((Boolean) e.getValue())) {
|
||||||
|
itr.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean want(String name) {
|
||||||
|
return query == null || query.contains(name.toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Range {
|
||||||
|
private transient PermissionRange range;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private int min;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private int max;
|
||||||
|
|
||||||
|
Range(PermissionRange r) {
|
||||||
|
range = r;
|
||||||
|
min = r.getMin();
|
||||||
|
max = r.getMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return range.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.httpd.rpc.project;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.gerrit.httpd.RestApiServlet;
|
||||||
|
import com.google.gerrit.server.OutputFormat;
|
||||||
|
import com.google.gerrit.server.project.ListProjects;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class ListProjectsServlet extends RestApiServlet {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final ParameterParser paramParser;
|
||||||
|
private final Provider<ListProjects> factory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ListProjectsServlet(ParameterParser paramParser, Provider<ListProjects> ls) {
|
||||||
|
this.paramParser = paramParser;
|
||||||
|
this.factory = ls;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws IOException {
|
||||||
|
ListProjects impl = factory.get();
|
||||||
|
if (!Strings.isNullOrEmpty(req.getPathInfo())) {
|
||||||
|
impl.setMatchPrefix(URLDecoder.decode(req.getPathInfo(), "UTF-8"));
|
||||||
|
}
|
||||||
|
if (acceptsJson(req)) {
|
||||||
|
impl.setFormat(OutputFormat.JSON_COMPACT);
|
||||||
|
}
|
||||||
|
if (paramParser.parse(impl, req, res)) {
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
if (impl.getFormat().isJson()) {
|
||||||
|
res.setContentType(JSON_TYPE);
|
||||||
|
buf.write(JSON_MAGIC);
|
||||||
|
} else {
|
||||||
|
res.setContentType("text/plain");
|
||||||
|
}
|
||||||
|
impl.display(buf);
|
||||||
|
res.setCharacterEncoding("UTF-8");
|
||||||
|
send(req, res, buf.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright (C) 2012 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.
|
||||||
|
|
||||||
|
package com.google.gerrit.server;
|
||||||
|
|
||||||
|
import com.google.gson.FieldNamingPolicy;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gwtjsonrpc.server.SqlTimestampDeserializer;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
/** Standard output format used by an API call. */
|
||||||
|
public enum OutputFormat {
|
||||||
|
/**
|
||||||
|
* The output is a human readable text format. It may also be regular enough
|
||||||
|
* to be machine readable. Whether or not the text format is machine readable
|
||||||
|
* and will be committed to as a long term format that tools can build upon is
|
||||||
|
* specific to each API call.
|
||||||
|
*/
|
||||||
|
TEXT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty-printed JSON format. This format uses whitespace to make the output
|
||||||
|
* readable by a human, but is also machine readable with a JSON library. The
|
||||||
|
* structure of the output is a long term format that tools can rely upon.
|
||||||
|
*/
|
||||||
|
JSON,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #JSON}, but with unnecessary whitespace removed to save
|
||||||
|
* generation time and copy costs. Typically JSON_COMPACT format is used by a
|
||||||
|
* browser based HTML client running over the network.
|
||||||
|
*/
|
||||||
|
JSON_COMPACT;
|
||||||
|
|
||||||
|
/** @return true when the format is either JSON or JSON_COMPACT. */
|
||||||
|
public boolean isJson() {
|
||||||
|
return this == JSON_COMPACT || this == JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return a new Gson instance configured according to the format. */
|
||||||
|
public GsonBuilder newGsonBuilder() {
|
||||||
|
if (!isJson()) {
|
||||||
|
throw new IllegalStateException(String.format("%s is not JSON", this));
|
||||||
|
}
|
||||||
|
GsonBuilder gb = new GsonBuilder()
|
||||||
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
|
.registerTypeAdapter(Timestamp.class, new SqlTimestampDeserializer());
|
||||||
|
if (this == OutputFormat.JSON) {
|
||||||
|
gb.setPrettyPrinting();
|
||||||
|
}
|
||||||
|
return gb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return a new Gson instance configured according to the format. */
|
||||||
|
public Gson newGson() {
|
||||||
|
return newGsonBuilder().create();
|
||||||
|
}
|
||||||
|
}
|
@@ -14,10 +14,14 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.project;
|
package com.google.gerrit.server.project;
|
||||||
|
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
|
import com.google.gerrit.reviewdb.client.Project.NameKey;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.OutputFormat;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gerrit.server.util.TreeFormatter;
|
import com.google.gerrit.server.util.TreeFormatter;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
@@ -36,6 +40,7 @@ import java.io.PrintWriter;
|
|||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@@ -75,6 +80,9 @@ public class ListProjects {
|
|||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final ProjectNode.Factory projectNodeFactory;
|
private final ProjectNode.Factory projectNodeFactory;
|
||||||
|
|
||||||
|
@Option(name = "--format", metaVar = "FMT", usage = "Output display format")
|
||||||
|
private OutputFormat format = OutputFormat.TEXT;
|
||||||
|
|
||||||
@Option(name = "--show-branch", aliases = {"-b"}, multiValued = true,
|
@Option(name = "--show-branch", aliases = {"-b"}, multiValued = true,
|
||||||
usage = "displays the sha of each project in the specified branch")
|
usage = "displays the sha of each project in the specified branch")
|
||||||
private List<String> showBranch;
|
private List<String> showBranch;
|
||||||
@@ -93,6 +101,11 @@ public class ListProjects {
|
|||||||
@Option(name = "--all", usage = "display all projects that are accessible by the calling user")
|
@Option(name = "--all", usage = "display all projects that are accessible by the calling user")
|
||||||
private boolean all;
|
private boolean all;
|
||||||
|
|
||||||
|
@Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "maximum number of projects to list")
|
||||||
|
private int limit;
|
||||||
|
|
||||||
|
private String matchPrefix;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ListProjects(CurrentUser currentUser, ProjectCache projectCache,
|
protected ListProjects(CurrentUser currentUser, ProjectCache projectCache,
|
||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
@@ -115,6 +128,20 @@ public class ListProjects {
|
|||||||
return showDescription;
|
return showDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OutputFormat getFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListProjects setFormat(OutputFormat fmt) {
|
||||||
|
this.format = fmt;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListProjects setMatchPrefix(String prefix) {
|
||||||
|
this.matchPrefix = prefix;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void display(OutputStream out) {
|
public void display(OutputStream out) {
|
||||||
final PrintWriter stdout;
|
final PrintWriter stdout;
|
||||||
try {
|
try {
|
||||||
@@ -124,10 +151,14 @@ public class ListProjects {
|
|||||||
throw new RuntimeException("JVM lacks UTF-8 encoding", e);
|
throw new RuntimeException("JVM lacks UTF-8 encoding", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
Map<String, ProjectInfo> output = Maps.newTreeMap();
|
||||||
|
Map<String, String> hiddenNames = Maps.newHashMap();
|
||||||
|
|
||||||
final TreeMap<Project.NameKey, ProjectNode> treeMap =
|
final TreeMap<Project.NameKey, ProjectNode> treeMap =
|
||||||
new TreeMap<Project.NameKey, ProjectNode>();
|
new TreeMap<Project.NameKey, ProjectNode>();
|
||||||
try {
|
try {
|
||||||
for (final Project.NameKey projectName : projectCache.all()) {
|
for (final Project.NameKey projectName : scan()) {
|
||||||
final ProjectState e = projectCache.get(projectName);
|
final ProjectState e = projectCache.get(projectName);
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
// If we can't get it from the cache, pretend its not present.
|
// If we can't get it from the cache, pretend its not present.
|
||||||
@@ -137,18 +168,39 @@ public class ListProjects {
|
|||||||
|
|
||||||
final ProjectControl pctl = e.controlFor(currentUser);
|
final ProjectControl pctl = e.controlFor(currentUser);
|
||||||
final boolean isVisible = pctl.isVisible() || (all && pctl.isOwner());
|
final boolean isVisible = pctl.isVisible() || (all && pctl.isOwner());
|
||||||
if (showTree) {
|
if (showTree && !format.isJson()) {
|
||||||
treeMap.put(projectName,
|
treeMap.put(projectName,
|
||||||
projectNodeFactory.create(pctl.getProject(), isVisible));
|
projectNodeFactory.create(pctl.getProject(), isVisible));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVisible) {
|
if (!isVisible && !(showTree && pctl.isOwner())) {
|
||||||
// Require the project itself to be visible to the user.
|
// Require the project itself to be visible to the user.
|
||||||
//
|
//
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectInfo info = new ProjectInfo();
|
||||||
|
info.name = projectName.get();
|
||||||
|
if (showTree && format.isJson()) {
|
||||||
|
ProjectState parent = e.getParentState();
|
||||||
|
if (parent != null) {
|
||||||
|
ProjectControl parentCtrl = parent.controlFor(currentUser);
|
||||||
|
if (parentCtrl.isVisible() || parentCtrl.isOwner()) {
|
||||||
|
info.parent = parent.getProject().getName();
|
||||||
|
} else {
|
||||||
|
info.parent = hiddenNames.get(parent.getProject().getName());
|
||||||
|
if (info.parent == null) {
|
||||||
|
info.parent = "?-" + (hiddenNames.size() + 1);
|
||||||
|
hiddenNames.put(parent.getProject().getName(), info.parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (showDescription && !e.getProject().getDescription().isEmpty()) {
|
||||||
|
info.description = e.getProject().getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (showBranch != null) {
|
if (showBranch != null) {
|
||||||
Repository git = repoManager.openRepository(projectName);
|
Repository git = repoManager.openRepository(projectName);
|
||||||
@@ -162,20 +214,19 @@ public class ListProjects {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Ref ref : refs) {
|
for (int i = 0; i < showBranch.size(); i++) {
|
||||||
if (ref == null) {
|
Ref ref = refs.get(i);
|
||||||
// Print stub (forty '-' symbols)
|
if (ref != null && ref.getObjectId() != null) {
|
||||||
stdout.print("----------------------------------------");
|
if (info.branches == null) {
|
||||||
} else {
|
info.branches = Maps.newLinkedHashMap();
|
||||||
stdout.print(ref.getObjectId().name());
|
}
|
||||||
|
info.branches.put(showBranch.get(i), ref.getObjectId().name());
|
||||||
}
|
}
|
||||||
stdout.print(' ');
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
git.close();
|
git.close();
|
||||||
}
|
}
|
||||||
|
} else if (!showTree && type != FilterType.ALL) {
|
||||||
} else if (type != FilterType.ALL) {
|
|
||||||
Repository git = repoManager.openRepository(projectName);
|
Repository git = repoManager.openRepository(projectName);
|
||||||
try {
|
try {
|
||||||
if (!type.matches(git)) {
|
if (!type.matches(git)) {
|
||||||
@@ -194,18 +245,40 @@ public class ListProjects {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout.print(projectName.get());
|
if (limit > 0 && ++found > limit) {
|
||||||
|
break;
|
||||||
String desc;
|
|
||||||
if (showDescription && !(desc = e.getProject().getDescription()).isEmpty()) {
|
|
||||||
// We still want to list every project as one-liners, hence escaping \n.
|
|
||||||
stdout.print(" - " + desc.replace("\n", "\\n"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout.print("\n");
|
if (format.isJson()) {
|
||||||
|
output.put(info.name, info);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showBranch != null) {
|
||||||
|
for (String name : showBranch) {
|
||||||
|
String ref = info.branches != null ? info.branches.get(name) : null;
|
||||||
|
if (ref == null) {
|
||||||
|
// Print stub (forty '-' symbols)
|
||||||
|
ref = "----------------------------------------";
|
||||||
|
}
|
||||||
|
stdout.print(ref);
|
||||||
|
stdout.print(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.print(info.name);
|
||||||
|
|
||||||
|
if (info.description != null) {
|
||||||
|
// We still want to list every project as one-liners, hence escaping \n.
|
||||||
|
stdout.print(" - " + info.description.replace("\n", "\\n"));
|
||||||
|
}
|
||||||
|
stdout.print('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTree && treeMap.size() > 0) {
|
if (format.isJson()) {
|
||||||
|
format.newGson().toJson(
|
||||||
|
output, new TypeToken<Map<String, ProjectInfo>>() {}.getType(), stdout);
|
||||||
|
stdout.print('\n');
|
||||||
|
} else if (showTree && treeMap.size() > 0) {
|
||||||
printProjectTree(stdout, treeMap);
|
printProjectTree(stdout, treeMap);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -213,6 +286,14 @@ public class ListProjects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Iterable<NameKey> scan() {
|
||||||
|
if (matchPrefix != null) {
|
||||||
|
return projectCache.byName(matchPrefix);
|
||||||
|
} else {
|
||||||
|
return projectCache.all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void printProjectTree(final PrintWriter stdout,
|
private void printProjectTree(final PrintWriter stdout,
|
||||||
final TreeMap<Project.NameKey, ProjectNode> treeMap) {
|
final TreeMap<Project.NameKey, ProjectNode> treeMap) {
|
||||||
final SortedSet<ProjectNode> sortedNodes = new TreeSet<ProjectNode>();
|
final SortedSet<ProjectNode> sortedNodes = new TreeSet<ProjectNode>();
|
||||||
@@ -270,4 +351,11 @@ public class ListProjects {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ProjectInfo {
|
||||||
|
transient String name;
|
||||||
|
String parent;
|
||||||
|
String description;
|
||||||
|
Map<String, String> branches;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.query;
|
package com.google.gerrit.server.query;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -44,23 +45,35 @@ import java.util.List;
|
|||||||
public abstract class Predicate<T> {
|
public abstract class Predicate<T> {
|
||||||
/** Combine the passed predicates into a single AND node. */
|
/** Combine the passed predicates into a single AND node. */
|
||||||
public static <T> Predicate<T> and(final Predicate<T>... that) {
|
public static <T> Predicate<T> and(final Predicate<T>... that) {
|
||||||
|
if (that.length == 1) {
|
||||||
|
return that[0];
|
||||||
|
}
|
||||||
return new AndPredicate<T>(that);
|
return new AndPredicate<T>(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Combine the passed predicates into a single AND node. */
|
/** Combine the passed predicates into a single AND node. */
|
||||||
public static <T> Predicate<T> and(
|
public static <T> Predicate<T> and(
|
||||||
final Collection<? extends Predicate<T>> that) {
|
final Collection<? extends Predicate<T>> that) {
|
||||||
|
if (that.size() == 1) {
|
||||||
|
return Iterables.getOnlyElement(that);
|
||||||
|
}
|
||||||
return new AndPredicate<T>(that);
|
return new AndPredicate<T>(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Combine the passed predicates into a single OR node. */
|
/** Combine the passed predicates into a single OR node. */
|
||||||
public static <T> Predicate<T> or(final Predicate<T>... that) {
|
public static <T> Predicate<T> or(final Predicate<T>... that) {
|
||||||
|
if (that.length == 1) {
|
||||||
|
return that[0];
|
||||||
|
}
|
||||||
return new OrPredicate<T>(that);
|
return new OrPredicate<T>(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Combine the passed predicates into a single OR node. */
|
/** Combine the passed predicates into a single OR node. */
|
||||||
public static <T> Predicate<T> or(
|
public static <T> Predicate<T> or(
|
||||||
final Collection<? extends Predicate<T>> that) {
|
final Collection<? extends Predicate<T>> that) {
|
||||||
|
if (that.size() == 1) {
|
||||||
|
return Iterables.getOnlyElement(that);
|
||||||
|
}
|
||||||
return new OrPredicate<T>(that);
|
return new OrPredicate<T>(that);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.server.query.change;
|
package com.google.gerrit.server.query.change;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.gerrit.common.data.ApprovalTypes;
|
import com.google.gerrit.common.data.ApprovalTypes;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
@@ -44,6 +45,7 @@ import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -206,10 +208,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ("draft".equalsIgnoreCase(value)) {
|
if ("draft".equalsIgnoreCase(value)) {
|
||||||
if (currentUser instanceof IdentifiedUser) {
|
return new HasDraftByPredicate(args.dbProvider, self());
|
||||||
return new HasDraftByPredicate(args.dbProvider,
|
|
||||||
((IdentifiedUser) currentUser).getAccountId());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
@@ -233,6 +232,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
return new IsReviewedPredicate(args.dbProvider);
|
return new IsReviewedPredicate(args.dbProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("owner".equalsIgnoreCase(value)) {
|
||||||
|
return new OwnerPredicate(args.dbProvider, self());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("reviewer".equalsIgnoreCase(value)) {
|
||||||
|
return new ReviewerPredicate(args.dbProvider, self());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return status(value);
|
return status(value);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@@ -303,42 +310,59 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> starredby(String who)
|
public Predicate<ChangeData> starredby(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException {
|
||||||
Account account = args.accountResolver.find(who);
|
if ("self".equals(who)) {
|
||||||
if (account == null) {
|
return new IsStarredByPredicate(args.dbProvider, currentUser);
|
||||||
throw error("User " + who + " not found");
|
|
||||||
}
|
}
|
||||||
return new IsStarredByPredicate(args.dbProvider, //
|
Set<Account.Id> m = parseAccount(who);
|
||||||
args.userFactory.create(args.dbProvider, account.getId()));
|
List<IsStarredByPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
|
for (Account.Id id : m) {
|
||||||
|
p.add(new IsStarredByPredicate(args.dbProvider,
|
||||||
|
args.userFactory.create(args.dbProvider, id)));
|
||||||
|
}
|
||||||
|
return Predicate.or(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> watchedby(String who)
|
public Predicate<ChangeData> watchedby(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException {
|
||||||
Account account = args.accountResolver.find(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
if (account == null) {
|
List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
throw error("User " + who + " not found");
|
for (Account.Id id : m) {
|
||||||
|
if (currentUser instanceof IdentifiedUser
|
||||||
|
&& id.equals(((IdentifiedUser) currentUser).getAccountId())) {
|
||||||
|
p.add(new IsWatchedByPredicate(args, currentUser));
|
||||||
|
} else {
|
||||||
|
p.add(new IsWatchedByPredicate(args,
|
||||||
|
args.userFactory.create(args.dbProvider, id)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new IsWatchedByPredicate(args, args.userFactory.create(
|
return Predicate.or(p);
|
||||||
args.dbProvider, account.getId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> draftby(String who) throws QueryParseException,
|
public Predicate<ChangeData> draftby(String who) throws QueryParseException,
|
||||||
OrmException {
|
OrmException {
|
||||||
Account account = args.accountResolver.find(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
if (account == null) {
|
List<HasDraftByPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
throw error("User " + who + " not found");
|
for (Account.Id id : m) {
|
||||||
|
p.add(new HasDraftByPredicate(args.dbProvider, id));
|
||||||
}
|
}
|
||||||
return new HasDraftByPredicate(args.dbProvider, account.getId());
|
return Predicate.or(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> visibleto(String who)
|
public Predicate<ChangeData> visibleto(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException {
|
||||||
Account account = args.accountResolver.find(who);
|
if ("self".equals(who)) {
|
||||||
if (account != null) {
|
return is_visible();
|
||||||
return visibleto(args.userFactory
|
}
|
||||||
.create(args.dbProvider, account.getId()));
|
Set<Account.Id> m = args.accountResolver.findAll(who);
|
||||||
|
if (!m.isEmpty()) {
|
||||||
|
List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
|
for (Account.Id id : m) {
|
||||||
|
return visibleto(args.userFactory.create(args.dbProvider, id));
|
||||||
|
}
|
||||||
|
return Predicate.or(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If its not an account, maybe its a group?
|
// If its not an account, maybe its a group?
|
||||||
@@ -375,24 +399,17 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> owner(String who) throws QueryParseException,
|
public Predicate<ChangeData> owner(String who) throws QueryParseException,
|
||||||
OrmException {
|
OrmException {
|
||||||
Set<Account.Id> m = args.accountResolver.findAll(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
if (m.isEmpty()) {
|
List<OwnerPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
throw error("User " + who + " not found");
|
for (Account.Id id : m) {
|
||||||
} else if (m.size() == 1) {
|
p.add(new OwnerPredicate(args.dbProvider, id));
|
||||||
Account.Id id = m.iterator().next();
|
|
||||||
return new OwnerPredicate(args.dbProvider, id);
|
|
||||||
} else {
|
|
||||||
List<OwnerPredicate> p = new ArrayList<OwnerPredicate>(m.size());
|
|
||||||
for (Account.Id id : m) {
|
|
||||||
p.add(new OwnerPredicate(args.dbProvider, id));
|
|
||||||
}
|
|
||||||
return Predicate.or(p);
|
|
||||||
}
|
}
|
||||||
|
return Predicate.or(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> ownerin(String group) throws QueryParseException,
|
public Predicate<ChangeData> ownerin(String group)
|
||||||
OrmException {
|
throws QueryParseException {
|
||||||
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
|
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
|
||||||
if (g == null) {
|
if (g == null) {
|
||||||
throw error("Group " + group + " not found");
|
throw error("Group " + group + " not found");
|
||||||
@@ -403,24 +420,17 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> reviewer(String who)
|
public Predicate<ChangeData> reviewer(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException {
|
||||||
Set<Account.Id> m = args.accountResolver.findAll(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
if (m.isEmpty()) {
|
List<ReviewerPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
throw error("User " + who + " not found");
|
for (Account.Id id : m) {
|
||||||
} else if (m.size() == 1) {
|
p.add(new ReviewerPredicate(args.dbProvider, id));
|
||||||
Account.Id id = m.iterator().next();
|
|
||||||
return new ReviewerPredicate(args.dbProvider, id);
|
|
||||||
} else {
|
|
||||||
List<ReviewerPredicate> p = new ArrayList<ReviewerPredicate>(m.size());
|
|
||||||
for (Account.Id id : m) {
|
|
||||||
p.add(new ReviewerPredicate(args.dbProvider, id));
|
|
||||||
}
|
|
||||||
return Predicate.or(p);
|
|
||||||
}
|
}
|
||||||
|
return Predicate.or(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> reviewerin(String group)
|
public Predicate<ChangeData> reviewerin(String group)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException {
|
||||||
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
|
AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
|
||||||
if (g == null) {
|
if (g == null) {
|
||||||
throw error("Group " + group + " not found");
|
throw error("Group " + group + " not found");
|
||||||
@@ -532,4 +542,23 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
throw error("Unsupported query:" + query);
|
throw error("Unsupported query:" + query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<Account.Id> parseAccount(String who)
|
||||||
|
throws QueryParseException, OrmException {
|
||||||
|
if ("self".equals(who)) {
|
||||||
|
return Collections.singleton(self());
|
||||||
|
}
|
||||||
|
Set<Account.Id> matches = args.accountResolver.findAll(who);
|
||||||
|
if (matches.isEmpty()) {
|
||||||
|
throw error("User " + who + " not found");
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Account.Id self() {
|
||||||
|
if (currentUser instanceof IdentifiedUser) {
|
||||||
|
return ((IdentifiedUser) currentUser).getAccountId();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -30,11 +30,13 @@ final class ListProjectsCommand extends BaseCommand {
|
|||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
parseCommandLine(impl);
|
parseCommandLine(impl);
|
||||||
if (impl.isShowTree() && (impl.getShowBranch() != null)) {
|
if (!impl.getFormat().isJson()) {
|
||||||
throw new UnloggedFailure(1, "fatal: --tree and --show-branch options are not compatible.");
|
if (impl.isShowTree() && (impl.getShowBranch() != null)) {
|
||||||
}
|
throw new UnloggedFailure(1, "fatal: --tree and --show-branch options are not compatible.");
|
||||||
if (impl.isShowTree() && impl.isShowDescription()) {
|
}
|
||||||
throw new UnloggedFailure(1, "fatal: --tree and --description options are not compatible.");
|
if (impl.isShowTree() && impl.isShowDescription()) {
|
||||||
|
throw new UnloggedFailure(1, "fatal: --tree and --description options are not compatible.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl.display(out);
|
impl.display(out);
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,7 @@ import org.kohsuke.args4j.NamedOptionDef;
|
|||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
import org.kohsuke.args4j.OptionDef;
|
import org.kohsuke.args4j.OptionDef;
|
||||||
import org.kohsuke.args4j.spi.BooleanOptionHandler;
|
import org.kohsuke.args4j.spi.BooleanOptionHandler;
|
||||||
|
import org.kohsuke.args4j.spi.EnumOptionHandler;
|
||||||
import org.kohsuke.args4j.spi.OptionHandler;
|
import org.kohsuke.args4j.spi.OptionHandler;
|
||||||
import org.kohsuke.args4j.spi.Setter;
|
import org.kohsuke.args4j.spi.Setter;
|
||||||
|
|
||||||
@@ -66,7 +67,6 @@ import java.util.ResourceBundle;
|
|||||||
* args4j style format prior to invoking args4j for parsing.
|
* args4j style format prior to invoking args4j for parsing.
|
||||||
*/
|
*/
|
||||||
public class CmdLineParser {
|
public class CmdLineParser {
|
||||||
|
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
CmdLineParser create(Object bean);
|
CmdLineParser create(Object bean);
|
||||||
}
|
}
|
||||||
@@ -118,6 +118,67 @@ public class CmdLineParser {
|
|||||||
out.write('\n');
|
out.write('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void printQueryStringUsage(String name, StringWriter out) {
|
||||||
|
out.write(name);
|
||||||
|
|
||||||
|
char next = '?';
|
||||||
|
List<NamedOptionDef> booleans = new ArrayList<NamedOptionDef>();
|
||||||
|
for (@SuppressWarnings("rawtypes") OptionHandler handler : parser.options) {
|
||||||
|
if (handler.option instanceof NamedOptionDef) {
|
||||||
|
NamedOptionDef n = (NamedOptionDef) handler.option;
|
||||||
|
|
||||||
|
if (handler instanceof BooleanOptionHandler) {
|
||||||
|
booleans.add(n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n.required()) {
|
||||||
|
out.write('[');
|
||||||
|
}
|
||||||
|
out.write(next);
|
||||||
|
next = '&';
|
||||||
|
if (n.name().startsWith("--")) {
|
||||||
|
out.write(n.name().substring(2));
|
||||||
|
} else if (n.name().startsWith("-")) {
|
||||||
|
out.write(n.name().substring(1));
|
||||||
|
} else {
|
||||||
|
out.write(n.name());
|
||||||
|
}
|
||||||
|
out.write('=');
|
||||||
|
|
||||||
|
String var = handler.getDefaultMetaVariable();
|
||||||
|
if (handler instanceof EnumOptionHandler) {
|
||||||
|
var = var.substring(1, var.length() - 1);
|
||||||
|
var = var.replaceAll(" ", "");
|
||||||
|
}
|
||||||
|
out.write(var);
|
||||||
|
if (!n.required()) {
|
||||||
|
out.write(']');
|
||||||
|
}
|
||||||
|
if (n.isMultiValued()) {
|
||||||
|
out.write('*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (NamedOptionDef n : booleans) {
|
||||||
|
if (!n.required()) {
|
||||||
|
out.write('[');
|
||||||
|
}
|
||||||
|
out.write(next);
|
||||||
|
next = '&';
|
||||||
|
if (n.name().startsWith("--")) {
|
||||||
|
out.write(n.name().substring(2));
|
||||||
|
} else if (n.name().startsWith("-")) {
|
||||||
|
out.write(n.name().substring(1));
|
||||||
|
} else {
|
||||||
|
out.write(n.name());
|
||||||
|
}
|
||||||
|
if (!n.required()) {
|
||||||
|
out.write(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean wasHelpRequestedByOption() {
|
public boolean wasHelpRequestedByOption() {
|
||||||
return parser.help.value;
|
return parser.help.value;
|
||||||
}
|
}
|
||||||
|
15
pom.xml
15
pom.xml
@@ -50,7 +50,7 @@ limitations under the License.
|
|||||||
<gwtormVersion>1.4</gwtormVersion>
|
<gwtormVersion>1.4</gwtormVersion>
|
||||||
<gwtjsonrpcVersion>1.3</gwtjsonrpcVersion>
|
<gwtjsonrpcVersion>1.3</gwtjsonrpcVersion>
|
||||||
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>
|
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>
|
||||||
<gwtVersion>2.3.0</gwtVersion>
|
<gwtVersion>2.4.0</gwtVersion>
|
||||||
<slf4jVersion>1.6.1</slf4jVersion>
|
<slf4jVersion>1.6.1</slf4jVersion>
|
||||||
<guiceVersion>3.0</guiceVersion>
|
<guiceVersion>3.0</guiceVersion>
|
||||||
<jettyVersion>7.2.1.v20101111</jettyVersion>
|
<jettyVersion>7.2.1.v20101111</jettyVersion>
|
||||||
@@ -363,7 +363,7 @@ limitations under the License.
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>gwt-maven-plugin</artifactId>
|
<artifactId>gwt-maven-plugin</artifactId>
|
||||||
<version>2.3.0</version>
|
<version>2.4.0</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -443,6 +443,12 @@ limitations under the License.
|
|||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>gwtorm</groupId>
|
<groupId>gwtorm</groupId>
|
||||||
<artifactId>gwtorm</artifactId>
|
<artifactId>gwtorm</artifactId>
|
||||||
@@ -824,11 +830,6 @@ limitations under the License.
|
|||||||
<url>http://download.java.net/maven/2/</url>
|
<url>http://download.java.net/maven/2/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
<repository>
|
|
||||||
<id>gson</id>
|
|
||||||
<url>https://google-gson.googlecode.com/svn/mavenrepo/</url>
|
|
||||||
</repository>
|
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>objectweb-repository</id>
|
<id>objectweb-repository</id>
|
||||||
<url>http://maven.objectweb.org/maven2/</url>
|
<url>http://maven.objectweb.org/maven2/</url>
|
||||||
|
Reference in New Issue
Block a user