blob: 48fd7ca1bbd655e8aedc65b8e7e6b04e83ec8961 [file] [log] [blame]
// Copyright (C) 2017 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.findowners;
import com.google.gerrit.extensions.annotations.PluginCanonicalWebUrl;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.googlesource.gerrit.plugins.findowners.Util.Owner2Weights;
import com.googlesource.gerrit.plugins.findowners.Util.String2String;
import com.googlesource.gerrit.plugins.findowners.Util.String2StringSet;
import com.googlesource.gerrit.plugins.findowners.Util.StringSet;
import java.util.Collection;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Create and return OWNERS info when "Find Owners" button is clicked. */
class Action implements UiAction<RevisionResource>,
RestModifyView<RevisionResource, Action.Input> {
private static final Logger log = LoggerFactory.getLogger(Action.class);
private Provider<CurrentUser> user; // is null if from HTTP request
private String url; // from REST request or client action call
private Server server;
static class Input {
// Only the change number is required.
int change; // Revision change number
String debug; // REST API parameter, 1, true/false, yes
String patchset; // REST API parameter, patchset number
Input(int change, String2String params) {
this.change = change;
debug = params.get("debug");
patchset = params.get("patchset");
// other keys in params are ignored
}
}
@Inject
Action(@PluginCanonicalWebUrl String url, Provider<CurrentUser> user) {
this.url = Util.normalizeURL(url); // replace "http:///" with "http://"
int n = this.url.indexOf("/plugins/");
if (n > 0) { // remove suffix "plugins/find-owners/...."
this.url = this.url.substring(0, n + 1);
}
this.user = user;
server = new Server();
}
/** Used by unit tests to set up mocked Server. */
void setServer(Server s) {
server = s;
}
private String getUserName() {
return (null != user) ? user.get().getUserName() : "?";
}
private JsonArray getOwners(OwnersDb db, Collection<String> files) {
Owner2Weights weights = new Owner2Weights();
String2StringSet file2Owners = db.findOwners(files, weights);
JsonArray result = new JsonArray();
StringSet emails = new StringSet();
for (String key : OwnerWeights.sortKeys(weights)) {
if (!emails.contains(key)) {
result.add(key + " " + weights.get(key).encodeLevelCounts());
emails.add(key);
}
}
return result;
}
private void addNamedMap(JsonObject obj, String name,
Map<String, StringSet> map) {
JsonObject jsonMap = new JsonObject();
for (String key : Util.sort(map.keySet())) {
jsonMap.addProperty(key, String.join(" ", Util.sort(map.get(key))));
}
obj.add(name, jsonMap);
}
/** REST API to return owners info of a change. */
public JsonObject getChangeData(int change, String2String params) {
return apply(null, new Input(change, params));
}
/** Called by the client "Find Owners" button. */
@Override
public JsonObject apply(RevisionResource rev, Input input) {
server.setChangeId(url, input.change);
String error = (null != server.error)
? server.error : server.setPatchId(input.patchset);
if (null != error) {
JsonObject obj = new JsonObject();
obj.addProperty("error", error);
return obj;
}
OwnersDb db = server.getCachedOwnersDb();
Collection<String> changedFiles = server.getChangedFiles();
String2StringSet file2Owners = db.findOwners(changedFiles);
JsonObject obj = new JsonObject();
obj.addProperty(Config.MIN_OWNER_VOTE_LEVEL, server.getMinOwnerVoteLevel());
boolean addDebugMsg = (null != input.debug)
? Util.parseBoolean(input.debug) : server.getAddDebugMsg();
obj.addProperty(Config.ADD_DEBUG_MSG, addDebugMsg);
obj.addProperty("change", input.change);
obj.addProperty("patchset", server.patchset);
obj.addProperty("owner_revision", db.revision);
if (addDebugMsg) {
JsonObject dbgMsgObj = new JsonObject();
dbgMsgObj.addProperty("user", getUserName());
dbgMsgObj.addProperty("project", server.project);
dbgMsgObj.addProperty("branch", server.branch);
dbgMsgObj.addProperty("server", url);
obj.add("dbgmsgs", dbgMsgObj);
addNamedMap(obj, "path2owners", db.path2Owners);
addNamedMap(obj, "owner2paths", db.owner2Paths);
}
addNamedMap(obj, "file2owners", file2Owners);
obj.add("reviewers", server.getReviewers());
obj.add("owners", getOwners(db, changedFiles));
obj.add("files", Util.newJsonArrayFromStrings(changedFiles));
return obj;
}
@Override
public Description getDescription(RevisionResource resource) {
int change = resource.getChange().getId().get();
server.setChangeId(url, change);
if (null == server.branch) {
log.error("Cannot get branch of change: " + change);
return null; // no "Find Owners" button
}
OwnersDb db = server.getCachedOwnersDb();
if (server.traceServerMsg()) {
log.info(server.genDebugMsg(db));
}
Status status = server.getStatus(resource);
// Commit message is not used to enable/disable "Find Owners".
boolean needFindOwners =
(null != user && user.get() instanceof IdentifiedUser)
&& (db.getNumOwners() > 0)
&& (status != Status.ABANDONED && status != Status.MERGED);
return new Description()
.setLabel("Find Owners")
.setTitle("Find owners to add to Reviewers list")
.setVisible(needFindOwners);
}
}