Initial implementation of dashboard API

Add an initial framework for the dashboard API.

There is no REST endpoint for creating a project dashboard; a new
dashboard can only be created manually by adding a config file to
the special refs/meta/dashboards ref [1]. Thus, the intial version
of the API only supports getting a named dashboard, and the test
serves only to ensure that the binding of the new API works, i.e.
does not cause any guice injection errors, etc.

Later commits will extend the API support:

- REST API and API to create a dashboard
- List dashboards
- etc

[1] http://gerrit-documentation.storage.googleapis.com/Documentation/2.14/user-dashboards.html#project-dashboards

Change-Id: I85d5f91fa27540e8942709c014bb51a0ec427eff
This commit is contained in:
David Pursehouse
2017-09-14 22:19:27 +09:00
parent 017781e534
commit d33ed29cf5
8 changed files with 184 additions and 2 deletions

View File

@@ -0,0 +1,29 @@
// 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.google.gerrit.acceptance.api.project;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import org.junit.Test;
@NoHttpd
public class DashboardIT extends AbstractDaemonTest {
@Test
public void defaultDashboardDoesNotExist() throws Exception {
exception.expect(ResourceNotFoundException.class);
gApi.projects().name(project.get()).dashboard("default").get();
}
}

View File

@@ -0,0 +1,41 @@
// 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.google.gerrit.extensions.api.projects;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface DashboardApi {
DashboardInfo get() throws RestApiException;
DashboardInfo get(boolean inherited) throws RestApiException;
/**
* A default implementation which allows source compatibility when adding new methods to the
* interface.
*/
class NotImplemented implements DashboardApi {
@Override
public DashboardInfo get() throws RestApiException {
throw new NotImplementedException();
}
@Override
public DashboardInfo get(boolean inherited) throws RestApiException {
throw new NotImplementedException();
}
}
}

View File

@@ -135,6 +135,14 @@ public interface ProjectApi {
*/ */
CommitApi commit(String commit) throws RestApiException; CommitApi commit(String commit) throws RestApiException;
/**
* Lookup a dashboard by its name.
*
* @param name the name.
* @return API for accessing the dashboard.
*/
DashboardApi dashboard(String name) throws RestApiException;
/** /**
* A default implementation which allows source compatibility when adding new methods to the * A default implementation which allows source compatibility when adding new methods to the
* interface. * interface.
@@ -239,5 +247,10 @@ public interface ProjectApi {
public CommitApi commit(String commit) throws RestApiException { public CommitApi commit(String commit) throws RestApiException {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@Override
public DashboardApi dashboard(String name) throws RestApiException {
throw new NotImplementedException();
}
} }
} }

View File

@@ -0,0 +1,76 @@
// 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.google.gerrit.server.api.projects;
import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
import com.google.gerrit.extensions.api.projects.DashboardApi;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
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.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.DashboardResource;
import com.google.gerrit.server.project.DashboardsCollection;
import com.google.gerrit.server.project.GetDashboard;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
public class DashboardApiImpl implements DashboardApi {
interface Factory {
DashboardApiImpl create(ProjectResource project, String name);
}
private final DashboardsCollection dashboards;
private final Provider<GetDashboard> getDashboard;
private final ProjectResource project;
private final String name;
@Inject
DashboardApiImpl(
DashboardsCollection dashboards,
Provider<GetDashboard> getDashboard,
@Assisted ProjectResource project,
@Assisted String name) {
this.dashboards = dashboards;
this.getDashboard = getDashboard;
this.project = project;
this.name = name;
}
@Override
public DashboardInfo get() throws RestApiException {
return get(false);
}
@Override
public DashboardInfo get(boolean inherited) throws RestApiException {
try {
return getDashboard.get().setInherited(inherited).apply(resource());
} catch (IOException | PermissionBackendException | ConfigInvalidException e) {
throw asRestApiException("Cannot read dashboard", e);
}
}
private DashboardResource resource()
throws ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
return dashboards.parse(project, IdString.fromDecoded(name));
}
}

View File

@@ -27,5 +27,6 @@ public class Module extends FactoryModule {
factory(ProjectApiImpl.Factory.class); factory(ProjectApiImpl.Factory.class);
factory(ChildProjectApiImpl.Factory.class); factory(ChildProjectApiImpl.Factory.class);
factory(CommitApiImpl.Factory.class); factory(CommitApiImpl.Factory.class);
factory(DashboardApiImpl.Factory.class);
} }
} }

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.extensions.api.projects.ChildProjectApi;
import com.google.gerrit.extensions.api.projects.CommitApi; import com.google.gerrit.extensions.api.projects.CommitApi;
import com.google.gerrit.extensions.api.projects.ConfigInfo; import com.google.gerrit.extensions.api.projects.ConfigInfo;
import com.google.gerrit.extensions.api.projects.ConfigInput; import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.api.projects.DashboardApi;
import com.google.gerrit.extensions.api.projects.DeleteBranchesInput; import com.google.gerrit.extensions.api.projects.DeleteBranchesInput;
import com.google.gerrit.extensions.api.projects.DeleteTagsInput; import com.google.gerrit.extensions.api.projects.DeleteTagsInput;
import com.google.gerrit.extensions.api.projects.DescriptionInput; import com.google.gerrit.extensions.api.projects.DescriptionInput;
@@ -96,6 +97,7 @@ public class ProjectApiImpl implements ProjectApi {
private final DeleteTags deleteTags; private final DeleteTags deleteTags;
private final CommitsCollection commitsCollection; private final CommitsCollection commitsCollection;
private final CommitApiImpl.Factory commitApi; private final CommitApiImpl.Factory commitApi;
private final DashboardApiImpl.Factory dashboardApi;
@AssistedInject @AssistedInject
ProjectApiImpl( ProjectApiImpl(
@@ -122,6 +124,7 @@ public class ProjectApiImpl implements ProjectApi {
DeleteTags deleteTags, DeleteTags deleteTags,
CommitsCollection commitsCollection, CommitsCollection commitsCollection,
CommitApiImpl.Factory commitApi, CommitApiImpl.Factory commitApi,
DashboardApiImpl.Factory dashboardApi,
@Assisted ProjectResource project) { @Assisted ProjectResource project) {
this( this(
user, user,
@@ -148,6 +151,7 @@ public class ProjectApiImpl implements ProjectApi {
project, project,
commitsCollection, commitsCollection,
commitApi, commitApi,
dashboardApi,
null); null);
} }
@@ -176,6 +180,7 @@ public class ProjectApiImpl implements ProjectApi {
DeleteTags deleteTags, DeleteTags deleteTags,
CommitsCollection commitsCollection, CommitsCollection commitsCollection,
CommitApiImpl.Factory commitApi, CommitApiImpl.Factory commitApi,
DashboardApiImpl.Factory dashboardApi,
@Assisted String name) { @Assisted String name) {
this( this(
user, user,
@@ -202,6 +207,7 @@ public class ProjectApiImpl implements ProjectApi {
null, null,
commitsCollection, commitsCollection,
commitApi, commitApi,
dashboardApi,
name); name);
} }
@@ -230,6 +236,7 @@ public class ProjectApiImpl implements ProjectApi {
ProjectResource project, ProjectResource project,
CommitsCollection commitsCollection, CommitsCollection commitsCollection,
CommitApiImpl.Factory commitApi, CommitApiImpl.Factory commitApi,
DashboardApiImpl.Factory dashboardApi,
String name) { String name) {
this.user = user; this.user = user;
this.permissionBackend = permissionBackend; this.permissionBackend = permissionBackend;
@@ -256,6 +263,7 @@ public class ProjectApiImpl implements ProjectApi {
this.commitsCollection = commitsCollection; this.commitsCollection = commitsCollection;
this.commitApi = commitApi; this.commitApi = commitApi;
this.createAccessChange = createAccessChange; this.createAccessChange = createAccessChange;
this.dashboardApi = dashboardApi;
} }
@Override @Override
@@ -447,6 +455,15 @@ public class ProjectApiImpl implements ProjectApi {
} }
} }
@Override
public DashboardApi dashboard(String name) throws RestApiException {
try {
return dashboardApi.create(checkExists(), name);
} catch (Exception e) {
throw asRestApiException("Cannot parse dashboard", e);
}
}
private ProjectResource checkExists() throws ResourceNotFoundException { private ProjectResource checkExists() throws ResourceNotFoundException {
if (project == null) { if (project == null) {
throw new ResourceNotFoundException(name); throw new ResourceNotFoundException(name);

View File

@@ -54,7 +54,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@Singleton @Singleton
class DashboardsCollection public class DashboardsCollection
implements ChildCollection<ProjectResource, DashboardResource>, AcceptsCreate<ProjectResource> { implements ChildCollection<ProjectResource, DashboardResource>, AcceptsCreate<ProjectResource> {
private static final String DEFAULT_DASHBOARD_NAME = "default"; private static final String DEFAULT_DASHBOARD_NAME = "default";

View File

@@ -33,7 +33,7 @@ import java.util.List;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.kohsuke.args4j.Option; import org.kohsuke.args4j.Option;
class GetDashboard implements RestReadView<DashboardResource> { public class GetDashboard implements RestReadView<DashboardResource> {
private final DashboardsCollection dashboards; private final DashboardsCollection dashboards;
@Option(name = "--inherited", usage = "include inherited dashboards") @Option(name = "--inherited", usage = "include inherited dashboards")
@@ -44,6 +44,11 @@ class GetDashboard implements RestReadView<DashboardResource> {
this.dashboards = dashboards; this.dashboards = dashboards;
} }
public GetDashboard setInherited(boolean inherited) {
this.inherited = inherited;
return this;
}
@Override @Override
public DashboardInfo apply(DashboardResource resource) public DashboardInfo apply(DashboardResource resource)
throws ResourceNotFoundException, ResourceConflictException, IOException, throws ResourceNotFoundException, ResourceConflictException, IOException,