Add a get dependency REST api
Add a REST api to get zuul cross project dependency info and refactor
the UI code to use this new api. This makes the UI code much
simpler.
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/DependencyInfo.java b/src/main/java/com/googlesource/gerrit/plugins/chound/DependencyInfo.java
new file mode 100644
index 0000000..347f2a1
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chound/DependencyInfo.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.chound;
+
+import java.util.List;
+
+public class DependencyInfo {
+ public List<String> dependsOn;
+ public List<String> neededBy;
+ public boolean cycle;
+}
\ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/GetDependency.java b/src/main/java/com/googlesource/gerrit/plugins/chound/GetDependency.java
new file mode 100644
index 0000000..3f0afeb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chound/GetDependency.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2015 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.googlesource.gerrit.plugins.chound;
+
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.query.change.QueryChanges;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Singleton
+public class GetDependency implements RestReadView<RevisionResource> {
+ private final ChangesCollection changes;
+ private final GitRepositoryManager repoManager;
+
+ @Inject
+ GetDependency(ChangesCollection changes, GitRepositoryManager repoManager) {
+ this.changes = changes;
+ this.repoManager = repoManager;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public DependencyInfo apply(RevisionResource rsrc)
+ throws RepositoryNotFoundException, IOException, BadRequestException,
+ AuthException, OrmException {
+
+ DependencyInfo out = new DependencyInfo();
+ out.dependsOn = new ArrayList<>();
+ out.neededBy = new ArrayList<>();
+
+ // get depends on info
+ Project.NameKey p = rsrc.getChange().getProject();
+ try (Repository repo = repoManager.openRepository(p);
+ RevWalk rw = new RevWalk(repo)) {
+ String rev = rsrc.getPatchSet().getRevision().get();
+ RevCommit commit = rw.parseCommit(ObjectId.fromString(rev));
+ String commitMsg = commit.getFullMessage();
+ Pattern pattern = Pattern.compile("[Dd]epends-[Oo]n:? (I[0-9a-f]{8,40})",
+ Pattern.DOTALL);
+ Matcher matcher = pattern.matcher(commitMsg);
+ while (matcher.find()) {
+ out.dependsOn.add(matcher.group(1));
+ }
+ }
+
+ // get needed by info
+ Change.Key chgKey = rsrc.getChange().getKey();
+ QueryChanges query = changes.list();
+ String neededByQuery = "message:" + chgKey + " -change:" + chgKey;
+ query.addQuery(neededByQuery);
+ List<ChangeInfo> changes =
+ (List<ChangeInfo>) query.apply(TopLevelResource.INSTANCE);
+ // check for dependency cycles
+ for (ChangeInfo change : changes) {
+ if (out.dependsOn.contains(change.changeId)) {
+ out.cycle = true;
+ }
+ out.neededBy.add(change.changeId);
+ }
+
+ return out;
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/Module.java b/src/main/java/com/googlesource/gerrit/plugins/chound/Module.java
index 460d954..13fc143 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chound/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chound/Module.java
@@ -14,6 +14,8 @@
package com.googlesource.gerrit.plugins.chound;
+import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND;
+
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.inject.AbstractModule;
@@ -24,6 +26,7 @@
install(new RestApiModule() {
@Override
protected void configure() {
+ get(REVISION_KIND, "dependency").to(GetDependency.class);
}
});
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/client/DependencyInfo.java b/src/main/java/com/googlesource/gerrit/plugins/chound/client/DependencyInfo.java
new file mode 100644
index 0000000..e1d163c
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/chound/client/DependencyInfo.java
@@ -0,0 +1,14 @@
+package com.googlesource.gerrit.plugins.chound.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArrayString;
+
+public class DependencyInfo extends JavaScriptObject {
+
+ public final native JsArrayString dependsOn() /*-{ return this.depends_on; }-*/;
+ public final native JsArrayString neededBy() /*-{ return this.needed_by; }-*/;
+ public final native boolean cycle() /*-{ return this.cycle; }-*/;
+
+ protected DependencyInfo() {
+ }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/client/LabelPanel.java b/src/main/java/com/googlesource/gerrit/plugins/chound/client/LabelPanel.java
index 9be57e6..c12ceb8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/chound/client/LabelPanel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/chound/client/LabelPanel.java
@@ -16,13 +16,10 @@
import com.google.gerrit.client.GerritUiExtensionPoint;
import com.google.gerrit.client.info.ChangeInfo;
-import com.google.gerrit.client.info.ChangeInfo.CommitInfo;
import com.google.gerrit.client.info.ChangeInfo.RevisionInfo;
-import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.plugin.client.Plugin;
import com.google.gerrit.plugin.client.extension.Panel;
import com.google.gerrit.plugin.client.rpc.RestApi;
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -45,30 +42,13 @@
final RevisionInfo rev =
panel.getObject(GerritUiExtensionPoint.Key.REVISION_INFO).cast();
- // revision might not be available when using inline edit
- if (rev != null && !rev.isEdit()) {
- new RestApi("changes").id(change.id()).view("revisions").id(rev.id())
- .view("commit").get(new AsyncCallback<CommitInfo>() {
- @Override
- public void onSuccess(CommitInfo result) {
- if (result != null) {
- displayDependsOn(result);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // never invoked
- }
- });
- }
- new RestApi("changes").view("?q=message:" + change.changeId())
- .view("+NOT+change:" + change.changeId())
- .get(new AsyncCallback<NativeMap<ChangeInfo>>() {
+ new RestApi("changes").id(change.id()).view("revisions").id(rev.id())
+ .view(Plugin.get().getPluginName(), "dependency")
+ .get(new AsyncCallback<DependencyInfo>() {
@Override
- public void onSuccess(NativeMap<ChangeInfo> result) {
- if (result != null && !result.isEmpty()) {
- displayNeededBy(result);
+ public void onSuccess(DependencyInfo result) {
+ if (result != null) {
+ display(result);
}
}
@@ -79,41 +59,30 @@
});
}
- private void displayDependsOn(CommitInfo result) {
+ private void display(DependencyInfo result) {
int row = 0;
int column = 1;
Grid grid = new Grid(row, column);
- String message = result.message();
- if (message.toLowerCase().contains("depends-on:")) {
- MatchResult matcher;
- RegExp pattern =
- RegExp.compile("[Dd]epends-[Oo]n:? (I[0-9a-f]{8,40})", "g");
- while ((matcher = pattern.exec(message)) != null) {
- HorizontalPanel p = new HorizontalPanel();
- p.addStyleName("infoBlock");
- Label label = new Label("Depends-on");
- label.setWidth("72px");
- p.add(label);
- p.add(new CopyableLabel(matcher.getGroup(1)));
- grid.insertRow(row);
- grid.setWidget(row, 0, p);
- row++;
- }
- add(grid);
+ // show depends-on ids
+ for (int i=0; i < result.dependsOn().length(); i++) {
+ HorizontalPanel p = new HorizontalPanel();
+ p.addStyleName("infoBlock");
+ Label label = new Label("Depends-on");
+ label.setWidth("72px");
+ p.add(label);
+ p.add(new CopyableLabel(result.dependsOn().get(i)));
+ grid.insertRow(row);
+ grid.setWidget(row, 0, p);
+ row++;
}
- }
-
- private void displayNeededBy(NativeMap<ChangeInfo> result) {
- int row = 0;
- int column = 1;
- Grid grid = new Grid(row, column);
- for (String key : result.keySet()) {
+ // show needed-by ids
+ for (int i=0; i < result.neededBy().length(); i++) {
HorizontalPanel p = new HorizontalPanel();
p.addStyleName("infoBlock");
Label label = new Label("Needed-by");
label.setWidth("72px");
p.add(label);
- p.add(new CopyableLabel(result.get(key).changeId()));
+ p.add(new CopyableLabel(result.neededBy().get(i)));
grid.insertRow(row);
grid.setWidget(row, 0, p);
row++;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/chound/chound.css b/src/main/java/com/googlesource/gerrit/plugins/chound/public/chound.css
similarity index 100%
rename from src/main/java/com/googlesource/gerrit/plugins/chound/chound.css
rename to src/main/java/com/googlesource/gerrit/plugins/chound/public/chound.css
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
index 3fd5340..fa3a50d 100644
--- a/src/main/resources/Documentation/about.md
+++ b/src/main/resources/Documentation/about.md
@@ -1,3 +1 @@
-The @PLUGIN@ plugin tracks down referenced Gerrit changes from the
-commit message and displays it on the change screen.
-
+The @PLUGIN@ plugin shows Zuul cross project dependencies on the Gerrit UI.
diff --git a/src/main/resources/Documentation/rest-api-changes.md b/src/main/resources/Documentation/rest-api-changes.md
new file mode 100644
index 0000000..c35fa3a
--- /dev/null
+++ b/src/main/resources/Documentation/rest-api-changes.md
@@ -0,0 +1,70 @@
+@PLUGIN@ - /changes/ REST API
+==============================
+
+This page describes the '/changes/' REST endpoints that are added by
+the @PLUGIN@ plugin.
+
+Please also take note of the general information on the
+[REST API](../../../Documentation/rest-api.html).
+
+<a id="plugin-endpoints"> @PLUGIN@ Endpoints
+--------------------------------------------
+
+### <a id="get-dependency"> Get Dependency
+
+__GET__ /changes/{change-id}/revisions/{revision-id}/@PLUGIN@~dependency
+
+Gets the zuul [dependency](#dependency-info) for a change. Please refer to the
+general [changes rest api](../../../Documentation/rest-api-changes.html#get-review)
+for additional info on this request.
+
+#### Request
+
+```
+ GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/@PLUGIN@~dependency HTTP/1.0
+```
+
+#### Response
+
+```
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "depends_on": [
+ "Ic79ed94daa9b58527139aadba1b0d59d1f54754b",
+ "I66853bf0c18e60f8de14d44dfb7c2ca1c3793111"
+ ],
+ "needed_by": [
+ "I66853bf0c18e60f8de14d44dfb7c2ca1c379311d"
+ ],
+ "cycle": false
+ }
+```
+
+<a id="json-entities">JSON Entities
+-----------------------------------
+
+### <a id="dependency-info"></a>DependencyInfo
+
+The `DependencyInfo` entity shows zuul dependencies on a patch set.
+
+|Field Name |Description|
+|:----------|:----------|
+|depends_on |List of changes that this change depends on|
+|needed-by |List of changes that is dependent on this change|
+|cycle |Whether this change is in a circular dependency chain|
+
+
+SEE ALSO
+--------
+
+* [Change related REST endpoints](../../../Documentation/rest-api-changes.html)
+* [Plugin Development](../../../Documentation/dev-plugins.html)
+* [REST API Development](../../../Documentation/dev-rest-api.html)
+
+GERRIT
+------
+Part of [Gerrit Code Review](../../../Documentation/index.html)