// 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.project; import static com.google.gerrit.reviewdb.client.RefNames.REFS_DASHBOARDS; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.restapi.AcceptsCreate; import com.google.gerrit.extensions.restapi.ChildCollection; import com.google.gerrit.extensions.restapi.IdString; import com.google.gerrit.extensions.restapi.ResourceNotFoundException; import com.google.gerrit.extensions.restapi.RestApiException; import com.google.gerrit.extensions.restapi.RestModifyView; import com.google.gerrit.extensions.restapi.RestView; import com.google.gerrit.extensions.restapi.Url; import com.google.gerrit.reviewdb.client.Project; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.UrlEncoded; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gson.annotations.SerializedName; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.BlobBasedConfig; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; @Singleton class DashboardsCollection implements ChildCollection<ProjectResource, DashboardResource>, AcceptsCreate<ProjectResource> { private final GitRepositoryManager gitManager; private final DynamicMap<RestView<DashboardResource>> views; private final Provider<ListDashboards> list; private final Provider<SetDefaultDashboard.CreateDefault> createDefault; @Inject DashboardsCollection(GitRepositoryManager gitManager, DynamicMap<RestView<DashboardResource>> views, Provider<ListDashboards> list, Provider<SetDefaultDashboard.CreateDefault> createDefault) { this.gitManager = gitManager; this.views = views; this.list = list; this.createDefault = createDefault; } @Override public RestView<ProjectResource> list() throws ResourceNotFoundException { return list.get(); } @SuppressWarnings("unchecked") @Override public RestModifyView<ProjectResource, ?> create(ProjectResource parent, IdString id) throws RestApiException { if (id.equals("default")) { return createDefault.get(); } throw new ResourceNotFoundException(id); } @Override public DashboardResource parse(ProjectResource parent, IdString id) throws ResourceNotFoundException, IOException, ConfigInvalidException { ProjectControl myCtl = parent.getControl(); if (id.equals("default")) { return DashboardResource.projectDefault(myCtl); } List<String> parts = Lists.newArrayList( Splitter.on(':').limit(2).split(id.get())); if (parts.size() != 2) { throw new ResourceNotFoundException(id); } CurrentUser user = myCtl.getCurrentUser(); String ref = parts.get(0); String path = parts.get(1); for (ProjectState ps : myCtl.getProjectState().tree()) { try { return parse(ps.controlFor(user), ref, path, myCtl); } catch (AmbiguousObjectException e) { throw new ResourceNotFoundException(id); } catch (IncorrectObjectTypeException e) { throw new ResourceNotFoundException(id); } catch (ConfigInvalidException e) { throw new ResourceNotFoundException(id); } catch (ResourceNotFoundException e) { continue; } } throw new ResourceNotFoundException(id); } private DashboardResource parse(ProjectControl ctl, String ref, String path, ProjectControl myCtl) throws ResourceNotFoundException, IOException, AmbiguousObjectException, IncorrectObjectTypeException, ConfigInvalidException { String id = ref + ":" + path; if (!ref.startsWith(REFS_DASHBOARDS)) { ref = REFS_DASHBOARDS + ref; } if (!Repository.isValidRefName(ref) || !ctl.controlForRef(ref).canRead()) { throw new ResourceNotFoundException(id); } Repository git; try { git = gitManager.openRepository(ctl.getProject().getNameKey()); } catch (RepositoryNotFoundException e) { throw new ResourceNotFoundException(id); } try { ObjectId objId = git.resolve(ref + ":" + path); if (objId == null) { throw new ResourceNotFoundException(id); } BlobBasedConfig cfg = new BlobBasedConfig(null, git, objId); return new DashboardResource(myCtl, ref, path, cfg, false); } finally { git.close(); } } @Override public DynamicMap<RestView<DashboardResource>> views() { return views; } static DashboardInfo parse(Project definingProject, String refName, String path, Config config, String project, boolean setDefault) throws UnsupportedEncodingException { DashboardInfo info = new DashboardInfo(refName, path); info.project = project; info.definingProject = definingProject.getName(); String query = config.getString("dashboard", null, "title"); info.title = replace(project, query == null ? info.path : query); info.description = replace(project, config.getString("dashboard", null, "description")); info.foreach = config.getString("dashboard", null, "foreach"); if (setDefault) { String id = refName + ":" + path; info.isDefault = id.equals(defaultOf(definingProject)) ? true : null; } UrlEncoded u = new UrlEncoded("/dashboard/"); u.put("title", Objects.firstNonNull(info.title, info.path)); if (info.foreach != null) { u.put("foreach", replace(project, info.foreach)); } for (String name : config.getSubsections("section")) { Section s = new Section(); s.name = name; s.query = config.getString("section", name, "query"); u.put(s.name, replace(project, s.query)); info.sections.add(s); } info.url = u.toString().replace("%3A", ":"); return info; } private static String replace(String project, String query) { return query.replace("${project}", project); } private static String defaultOf(Project proj) { final String defaultId = Objects.firstNonNull( proj.getLocalDefaultDashboard(), Strings.nullToEmpty(proj.getDefaultDashboard())); if (defaultId.startsWith(REFS_DASHBOARDS)) { return defaultId.substring(REFS_DASHBOARDS.length()); } else { return defaultId; } } static class DashboardInfo { String id; String project; String definingProject; String ref; String path; String description; String foreach; String url; @SerializedName("default") Boolean isDefault; String title; List<Section> sections = Lists.newArrayList(); DashboardInfo(String ref, String name) throws UnsupportedEncodingException { this.ref = ref; this.path = name; this.id = Joiner.on(':').join(Url.encode(ref), Url.encode(path)); } } static class Section { String name; String query; } }